@strapi/strapi 4.0.0-next.6 → 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 +41 -60
- package/lib/Strapi.js +234 -114
- 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 +17 -18
- 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 -8
- package/lib/container.js +6 -6
- package/lib/core/app-configuration/config-loader.js +1 -37
- package/lib/core/app-configuration/index.js +6 -46
- package/lib/core/app-configuration/load-config-file.js +43 -0
- package/lib/core/bootstrap.js +5 -117
- package/lib/core/domain/component/index.js +24 -0
- package/lib/core/domain/component/validator.js +29 -0
- package/lib/core/domain/content-type/index.js +140 -0
- package/lib/core/domain/content-type/validator.js +64 -0
- package/lib/core/domain/module/index.js +108 -0
- package/lib/core/domain/module/validation.js +33 -0
- package/lib/core/loaders/admin.js +16 -0
- package/lib/core/loaders/apis.js +159 -0
- package/lib/core/{load-components.js → loaders/components.js} +5 -7
- package/lib/core/loaders/index.js +11 -0
- package/lib/core/loaders/middlewares.js +36 -0
- package/lib/core/loaders/plugins/get-enabled-plugins.js +116 -0
- package/lib/core/loaders/plugins/index.js +123 -0
- package/lib/core/loaders/policies.js +28 -0
- package/lib/core/loaders/src-index.js +39 -0
- package/lib/core/registries/apis.js +29 -0
- package/lib/core/{app-configuration/config-provider.js → registries/config.js} +4 -11
- package/lib/core/registries/content-types.js +97 -0
- package/lib/core/registries/controllers.d.ts +7 -0
- package/lib/core/registries/controllers.js +114 -0
- 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 +89 -0
- package/lib/core/registries/modules.js +44 -0
- package/lib/core/registries/plugins.js +28 -0
- package/lib/core/registries/policies.d.ts +9 -0
- package/lib/core/registries/policies.js +89 -0
- package/lib/core/registries/services.d.ts +7 -0
- package/lib/core/registries/services.js +114 -0
- package/lib/core/utils.js +35 -0
- package/lib/core-api/controller/collection-type.js +45 -26
- package/lib/core-api/controller/index.d.ts +25 -0
- package/lib/core-api/controller/index.js +33 -11
- package/lib/core-api/controller/single-type.js +29 -15
- package/lib/core-api/controller/transform.js +62 -6
- package/lib/core-api/routes/index.js +71 -0
- package/lib/core-api/service/collection-type.js +43 -21
- package/lib/core-api/service/index.d.ts +21 -0
- package/lib/core-api/service/index.js +8 -67
- package/lib/core-api/service/pagination.js +130 -0
- package/lib/core-api/service/single-type.js +17 -19
- 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 -117
- 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 -51
- 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 +120 -59
- package/lib/services/entity-service/params.js +52 -94
- 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/{core → services}/fs.js +10 -2
- package/lib/services/metrics/index.js +41 -38
- package/lib/services/metrics/sender.js +2 -2
- 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/utils/upload-files.js +1 -1
- 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/is-initialized.js +1 -1
- package/lib/utils/run-checks.js +0 -15
- package/lib/utils/signals.js +24 -0
- package/lib/utils/startup-logger.js +2 -2
- package/lib/utils/update-notifier/index.js +3 -2
- package/package.json +93 -96
- package/lib/commands/generate.js +0 -76
- package/lib/core/index.js +0 -17
- package/lib/core/load-apis.js +0 -20
- package/lib/core/load-extensions.js +0 -71
- package/lib/core/load-functions.js +0 -21
- package/lib/core/load-middlewares.js +0 -130
- package/lib/core/load-modules.js +0 -55
- package/lib/core/load-plugins.js +0 -68
- package/lib/core/load-policies.js +0 -36
- package/lib/core/walk.js +0 -27
- package/lib/core-api/index.js +0 -39
- package/lib/load/check-reserved-filename.js +0 -10
- package/lib/load/load-config-files.js +0 -22
- package/lib/load/require-file-parse.js +0 -15
- 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 -71
- 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 -64
- package/lib/middlewares/router/utils/composeEndpoint.js +0 -25
- package/lib/middlewares/router/utils/routerChecker.js +0 -92
- package/lib/utils/get-prefixed-dependencies.js +0 -7
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { cloneDeep } = require('lodash/fp');
|
|
4
|
+
const _ = require('lodash');
|
|
5
|
+
const { hasDraftAndPublish, getPrivateAttributes } = require('@strapi/utils').contentTypes;
|
|
6
|
+
const {
|
|
7
|
+
CREATED_AT_ATTRIBUTE,
|
|
8
|
+
UPDATED_AT_ATTRIBUTE,
|
|
9
|
+
PUBLISHED_AT_ATTRIBUTE,
|
|
10
|
+
CREATED_BY_ATTRIBUTE,
|
|
11
|
+
UPDATED_BY_ATTRIBUTE,
|
|
12
|
+
} = require('@strapi/utils').contentTypes.constants;
|
|
13
|
+
const { validateContentTypeDefinition } = require('./validator');
|
|
14
|
+
|
|
15
|
+
const createContentType = (uid, definition) => {
|
|
16
|
+
try {
|
|
17
|
+
validateContentTypeDefinition(definition);
|
|
18
|
+
} catch (e) {
|
|
19
|
+
throw new Error(`Content Type Definition is invalid for ${uid}'.\n${e.errors}`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const { schema, actions, lifecycles } = cloneDeep(definition);
|
|
23
|
+
|
|
24
|
+
// general info
|
|
25
|
+
Object.assign(schema, {
|
|
26
|
+
kind: schema.kind || 'collectionType',
|
|
27
|
+
__schema__: pickSchema(definition.schema),
|
|
28
|
+
modelType: 'contentType',
|
|
29
|
+
modelName: definition.schema.info.singularName,
|
|
30
|
+
connection: 'default',
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
if (uid.startsWith('api::')) {
|
|
34
|
+
Object.assign(schema, {
|
|
35
|
+
uid,
|
|
36
|
+
apiName: uid.split('::')[1].split('.')[0],
|
|
37
|
+
collectionName: schema.collectionName || schema.info.singularName,
|
|
38
|
+
globalId: getGlobalId(schema, schema.info.singularName),
|
|
39
|
+
});
|
|
40
|
+
} else if (uid.startsWith('plugin::')) {
|
|
41
|
+
const pluginName = uid.split('::')[1].split('.')[0];
|
|
42
|
+
Object.assign(schema, {
|
|
43
|
+
uid,
|
|
44
|
+
plugin: pluginName, // TODO: to be set in load-plugins.js
|
|
45
|
+
collectionName:
|
|
46
|
+
schema.collectionName || `${pluginName}_${schema.info.singularName}`.toLowerCase(),
|
|
47
|
+
globalId: getGlobalId(schema, schema.info.singularName, pluginName),
|
|
48
|
+
});
|
|
49
|
+
} else if (uid.startsWith('admin::')) {
|
|
50
|
+
Object.assign(schema, {
|
|
51
|
+
uid,
|
|
52
|
+
plugin: 'admin',
|
|
53
|
+
globalId: getGlobalId(schema, schema.info.singularName, 'admin'),
|
|
54
|
+
});
|
|
55
|
+
} else {
|
|
56
|
+
throw new Error(
|
|
57
|
+
`Incorrect Content Type UID "${uid}". The UID should start with api::, plugin:: or strapi::.`
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
Object.defineProperty(schema, 'privateAttributes', {
|
|
62
|
+
get() {
|
|
63
|
+
return getPrivateAttributes(schema);
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// attributes
|
|
68
|
+
Object.assign(schema.attributes, {
|
|
69
|
+
[CREATED_AT_ATTRIBUTE]: {
|
|
70
|
+
type: 'datetime',
|
|
71
|
+
},
|
|
72
|
+
// TODO: handle on edit set to new date
|
|
73
|
+
[UPDATED_AT_ATTRIBUTE]: {
|
|
74
|
+
type: 'datetime',
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
if (hasDraftAndPublish(schema)) {
|
|
79
|
+
schema.attributes[PUBLISHED_AT_ATTRIBUTE] = {
|
|
80
|
+
type: 'datetime',
|
|
81
|
+
configurable: false,
|
|
82
|
+
writable: true,
|
|
83
|
+
visible: false,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const isPrivate = !_.get(schema, 'options.populateCreatorFields', false);
|
|
88
|
+
|
|
89
|
+
schema.attributes[CREATED_BY_ATTRIBUTE] = {
|
|
90
|
+
type: 'relation',
|
|
91
|
+
relation: 'oneToOne',
|
|
92
|
+
target: 'admin::user',
|
|
93
|
+
configurable: false,
|
|
94
|
+
writable: false,
|
|
95
|
+
visible: false,
|
|
96
|
+
useJoinTable: false,
|
|
97
|
+
private: isPrivate,
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
schema.attributes[UPDATED_BY_ATTRIBUTE] = {
|
|
101
|
+
type: 'relation',
|
|
102
|
+
relation: 'oneToOne',
|
|
103
|
+
target: 'admin::user',
|
|
104
|
+
configurable: false,
|
|
105
|
+
writable: false,
|
|
106
|
+
visible: false,
|
|
107
|
+
useJoinTable: false,
|
|
108
|
+
private: isPrivate,
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
Object.assign(schema, { actions, lifecycles });
|
|
112
|
+
|
|
113
|
+
return schema;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const getGlobalId = (model, modelName, prefix) => {
|
|
117
|
+
let globalId = prefix ? `${prefix}-${modelName}` : modelName;
|
|
118
|
+
|
|
119
|
+
return model.globalId || _.upperFirst(_.camelCase(globalId));
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const pickSchema = model => {
|
|
123
|
+
const schema = _.cloneDeep(
|
|
124
|
+
_.pick(model, [
|
|
125
|
+
'connection',
|
|
126
|
+
'collectionName',
|
|
127
|
+
'info',
|
|
128
|
+
'options',
|
|
129
|
+
'pluginOptions',
|
|
130
|
+
'attributes',
|
|
131
|
+
])
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
schema.kind = model.kind || 'collectionType';
|
|
135
|
+
return schema;
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
module.exports = {
|
|
139
|
+
createContentType,
|
|
140
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { keyBy, mapValues } = require('lodash');
|
|
4
|
+
const { yup } = require('@strapi/utils');
|
|
5
|
+
|
|
6
|
+
const LIFECYCLES = [
|
|
7
|
+
'beforeCreate',
|
|
8
|
+
'afterCreate',
|
|
9
|
+
'beforeFindOne',
|
|
10
|
+
'afterFindOne',
|
|
11
|
+
'beforeFindMany',
|
|
12
|
+
'afterFindMany',
|
|
13
|
+
'beforeCount',
|
|
14
|
+
'afterCount',
|
|
15
|
+
'beforeCreateMany',
|
|
16
|
+
'afterCreateMany',
|
|
17
|
+
'beforeUpdate',
|
|
18
|
+
'afterUpdate',
|
|
19
|
+
'beforeUpdateMany',
|
|
20
|
+
'afterUpdateMany',
|
|
21
|
+
'beforeDelete',
|
|
22
|
+
'afterDelete',
|
|
23
|
+
'beforeDeleteMany',
|
|
24
|
+
'afterDeleteMany',
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
const lifecyclesShape = mapValues(keyBy(LIFECYCLES), () =>
|
|
28
|
+
yup
|
|
29
|
+
.mixed()
|
|
30
|
+
.nullable()
|
|
31
|
+
.isFunction()
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const contentTypeSchemaValidator = yup.object().shape({
|
|
35
|
+
schema: yup.object().shape({
|
|
36
|
+
info: yup
|
|
37
|
+
.object()
|
|
38
|
+
.shape({
|
|
39
|
+
displayName: yup.string().required(),
|
|
40
|
+
singularName: yup
|
|
41
|
+
.string()
|
|
42
|
+
.isKebabCase()
|
|
43
|
+
.required(),
|
|
44
|
+
pluralName: yup
|
|
45
|
+
.string()
|
|
46
|
+
.isKebabCase()
|
|
47
|
+
.required(),
|
|
48
|
+
})
|
|
49
|
+
.required(),
|
|
50
|
+
}),
|
|
51
|
+
actions: yup.object().onlyContainsFunctions(),
|
|
52
|
+
lifecycles: yup
|
|
53
|
+
.object()
|
|
54
|
+
.shape(lifecyclesShape)
|
|
55
|
+
.noUnknown(),
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const validateContentTypeDefinition = data => {
|
|
59
|
+
return contentTypeSchemaValidator.validateSync(data, { strict: true, abortEarly: false });
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
module.exports = {
|
|
63
|
+
validateContentTypeDefinition,
|
|
64
|
+
};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const _ = require('lodash');
|
|
4
|
+
const { removeNamespace } = require('../../utils');
|
|
5
|
+
const { validateModule } = require('./validation');
|
|
6
|
+
|
|
7
|
+
const uidToPath = uid => uid.replace('::', '.');
|
|
8
|
+
|
|
9
|
+
// Removes the namespace from a map with keys prefixed with a namespace
|
|
10
|
+
const removeNamespacedKeys = (map, namespace) => {
|
|
11
|
+
return _.mapKeys(map, (value, key) => removeNamespace(key, namespace));
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const defaultModule = {
|
|
15
|
+
config: {},
|
|
16
|
+
routes: [],
|
|
17
|
+
controllers: {},
|
|
18
|
+
services: {},
|
|
19
|
+
contentTypes: {},
|
|
20
|
+
policies: {},
|
|
21
|
+
middlewares: {},
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const createModule = (namespace, rawModule, strapi) => {
|
|
25
|
+
_.defaults(rawModule, defaultModule);
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
validateModule(rawModule);
|
|
29
|
+
} catch (e) {
|
|
30
|
+
throw new Error(`strapi-server.js is invalid for '${namespace}'.\n${e.errors.join('\n')}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const called = {};
|
|
34
|
+
return {
|
|
35
|
+
async bootstrap() {
|
|
36
|
+
if (called.bootstrap) {
|
|
37
|
+
throw new Error(`Bootstrap for ${namespace} has already been called`);
|
|
38
|
+
}
|
|
39
|
+
called.bootstrap = true;
|
|
40
|
+
await (rawModule.bootstrap && rawModule.bootstrap({ strapi }));
|
|
41
|
+
},
|
|
42
|
+
async register() {
|
|
43
|
+
if (called.register) {
|
|
44
|
+
throw new Error(`Register for ${namespace} has already been called`);
|
|
45
|
+
}
|
|
46
|
+
called.register = true;
|
|
47
|
+
await (rawModule.register && rawModule.register({ strapi }));
|
|
48
|
+
},
|
|
49
|
+
async destroy() {
|
|
50
|
+
if (called.destroy) {
|
|
51
|
+
throw new Error(`Destroy for ${namespace} has already been called`);
|
|
52
|
+
}
|
|
53
|
+
called.destroy = true;
|
|
54
|
+
await (rawModule.destroy && rawModule.destroy({ strapi }));
|
|
55
|
+
},
|
|
56
|
+
load() {
|
|
57
|
+
strapi.container.get('content-types').add(namespace, rawModule.contentTypes);
|
|
58
|
+
strapi.container.get('services').add(namespace, rawModule.services);
|
|
59
|
+
strapi.container.get('policies').add(namespace, rawModule.policies);
|
|
60
|
+
strapi.container.get('middlewares').add(namespace, rawModule.middlewares);
|
|
61
|
+
strapi.container.get('controllers').add(namespace, rawModule.controllers);
|
|
62
|
+
strapi.container.get('config').set(uidToPath(namespace), rawModule.config);
|
|
63
|
+
},
|
|
64
|
+
get routes() {
|
|
65
|
+
return rawModule.routes;
|
|
66
|
+
},
|
|
67
|
+
config(path, defaultValue) {
|
|
68
|
+
return strapi.container.get('config').get(`${uidToPath(namespace)}.${path}`, defaultValue);
|
|
69
|
+
},
|
|
70
|
+
contentType(ctName) {
|
|
71
|
+
return strapi.container.get('content-types').get(`${namespace}.${ctName}`);
|
|
72
|
+
},
|
|
73
|
+
get contentTypes() {
|
|
74
|
+
const contentTypes = strapi.container.get('content-types').getAll(namespace);
|
|
75
|
+
return removeNamespacedKeys(contentTypes, namespace);
|
|
76
|
+
},
|
|
77
|
+
service(serviceName) {
|
|
78
|
+
return strapi.container.get('services').get(`${namespace}.${serviceName}`);
|
|
79
|
+
},
|
|
80
|
+
get services() {
|
|
81
|
+
const services = strapi.container.get('services').getAll(namespace);
|
|
82
|
+
return removeNamespacedKeys(services, namespace);
|
|
83
|
+
},
|
|
84
|
+
policy(policyName) {
|
|
85
|
+
return strapi.container.get('policies').get(`${namespace}.${policyName}`);
|
|
86
|
+
},
|
|
87
|
+
get policies() {
|
|
88
|
+
const policies = strapi.container.get('policies').getAll(namespace);
|
|
89
|
+
return removeNamespacedKeys(policies, namespace);
|
|
90
|
+
},
|
|
91
|
+
middleware(middlewareName) {
|
|
92
|
+
return strapi.container.get('middlewares').get(`${namespace}.${middlewareName}`);
|
|
93
|
+
},
|
|
94
|
+
get middlewares() {
|
|
95
|
+
const middlewares = strapi.container.get('middlewares').getAll(namespace);
|
|
96
|
+
return removeNamespacedKeys(middlewares, namespace);
|
|
97
|
+
},
|
|
98
|
+
controller(controllerName) {
|
|
99
|
+
return strapi.container.get('controllers').get(`${namespace}.${controllerName}`);
|
|
100
|
+
},
|
|
101
|
+
get controllers() {
|
|
102
|
+
const controllers = strapi.container.get('controllers').getAll(namespace);
|
|
103
|
+
return removeNamespacedKeys(controllers, namespace);
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
module.exports = { createModule };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { yup } = require('@strapi/utils');
|
|
4
|
+
|
|
5
|
+
const strapiServerSchema = yup
|
|
6
|
+
.object()
|
|
7
|
+
.shape({
|
|
8
|
+
bootstrap: yup.mixed().isFunction(),
|
|
9
|
+
destroy: yup.mixed().isFunction(),
|
|
10
|
+
register: yup.mixed().isFunction(),
|
|
11
|
+
config: yup.object(),
|
|
12
|
+
routes: yup.lazy(value => {
|
|
13
|
+
if (Array.isArray(value)) {
|
|
14
|
+
return yup.array();
|
|
15
|
+
} else {
|
|
16
|
+
return yup.object();
|
|
17
|
+
}
|
|
18
|
+
}),
|
|
19
|
+
controllers: yup.object(),
|
|
20
|
+
services: yup.object(),
|
|
21
|
+
policies: yup.object(),
|
|
22
|
+
middlewares: yup.object(),
|
|
23
|
+
contentTypes: yup.object(),
|
|
24
|
+
})
|
|
25
|
+
.noUnknown();
|
|
26
|
+
|
|
27
|
+
const validateModule = data => {
|
|
28
|
+
return strapiServerSchema.validateSync(data, { strict: true, abortEarly: false });
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
module.exports = {
|
|
32
|
+
validateModule,
|
|
33
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const _ = require('lodash');
|
|
4
|
+
|
|
5
|
+
module.exports = strapi => {
|
|
6
|
+
strapi.admin = require('@strapi/admin/strapi-server');
|
|
7
|
+
|
|
8
|
+
strapi.container.get('services').add(`admin::`, strapi.admin.services);
|
|
9
|
+
strapi.container.get('controllers').add(`admin::`, strapi.admin.controllers);
|
|
10
|
+
strapi.container.get('content-types').add(`admin::`, strapi.admin.contentTypes);
|
|
11
|
+
strapi.container.get('policies').add(`admin::`, strapi.admin.policies);
|
|
12
|
+
strapi.container.get('middlewares').add(`admin::`, strapi.admin.middlewares);
|
|
13
|
+
|
|
14
|
+
const userAdminConfig = strapi.config.get('admin');
|
|
15
|
+
strapi.container.get('config').set('admin', _.merge(strapi.admin.config, userAdminConfig));
|
|
16
|
+
};
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { join, extname, basename } = require('path');
|
|
4
|
+
const { existsSync } = require('fs-extra');
|
|
5
|
+
const _ = require('lodash');
|
|
6
|
+
const fse = require('fs-extra');
|
|
7
|
+
const { isKebabCase } = require('@strapi/utils');
|
|
8
|
+
|
|
9
|
+
// to handle names with numbers in it we first check if it is already in kebabCase
|
|
10
|
+
const normalizeName = name => (isKebabCase(name) ? name : _.kebabCase(name));
|
|
11
|
+
|
|
12
|
+
const DEFAULT_CONTENT_TYPE = {
|
|
13
|
+
schema: {},
|
|
14
|
+
actions: {},
|
|
15
|
+
lifecycles: {},
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
module.exports = async strapi => {
|
|
19
|
+
if (!existsSync(strapi.dirs.api)) {
|
|
20
|
+
throw new Error('Missing api folder. Please create one at `./src/api`');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const apisFDs = await fse.readdir(strapi.dirs.api, { withFileTypes: true });
|
|
24
|
+
const apis = {};
|
|
25
|
+
|
|
26
|
+
// only load folders
|
|
27
|
+
for (const apiFD of apisFDs) {
|
|
28
|
+
if (apiFD.isDirectory()) {
|
|
29
|
+
const apiName = normalizeName(apiFD.name);
|
|
30
|
+
const api = await loadAPI(join(strapi.dirs.api, apiFD.name));
|
|
31
|
+
|
|
32
|
+
apis[apiName] = api;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
validateContentTypesUnicity(apis);
|
|
37
|
+
|
|
38
|
+
for (const apiName in apis) {
|
|
39
|
+
strapi.container.get('apis').add(apiName, apis[apiName]);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const validateContentTypesUnicity = apis => {
|
|
44
|
+
const allApisSchemas = Object.values(apis).flatMap(api => Object.values(api.contentTypes));
|
|
45
|
+
|
|
46
|
+
const names = [];
|
|
47
|
+
allApisSchemas.forEach(({ schema }) => {
|
|
48
|
+
if (schema.info.singularName) {
|
|
49
|
+
const singularName = _.kebabCase(schema.info.singularName);
|
|
50
|
+
if (names.includes(singularName)) {
|
|
51
|
+
throw new Error(`The singular name "${schema.info.singularName}" should be unique`);
|
|
52
|
+
}
|
|
53
|
+
names.push(singularName);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (schema.info.pluralName) {
|
|
57
|
+
const pluralName = _.kebabCase(schema.info.pluralName);
|
|
58
|
+
if (names.includes(pluralName)) {
|
|
59
|
+
throw new Error(`The plural name "${schema.info.pluralName}" should be unique`);
|
|
60
|
+
}
|
|
61
|
+
names.push(pluralName);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const loadAPI = async dir => {
|
|
67
|
+
const [
|
|
68
|
+
index,
|
|
69
|
+
config,
|
|
70
|
+
routes,
|
|
71
|
+
controllers,
|
|
72
|
+
services,
|
|
73
|
+
policies,
|
|
74
|
+
middlewares,
|
|
75
|
+
contentTypes,
|
|
76
|
+
] = await Promise.all([
|
|
77
|
+
loadIndex(dir),
|
|
78
|
+
loadDir(join(dir, 'config')),
|
|
79
|
+
loadDir(join(dir, 'routes')),
|
|
80
|
+
loadDir(join(dir, 'controllers')),
|
|
81
|
+
loadDir(join(dir, 'services')),
|
|
82
|
+
loadDir(join(dir, 'policies')),
|
|
83
|
+
loadDir(join(dir, 'middlewares')),
|
|
84
|
+
loadContentTypes(join(dir, 'content-types')),
|
|
85
|
+
]);
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
...(index || {}),
|
|
89
|
+
config: config || {},
|
|
90
|
+
routes: routes || [],
|
|
91
|
+
controllers: controllers || {},
|
|
92
|
+
services: services || {},
|
|
93
|
+
policies: policies || {},
|
|
94
|
+
middlewares: middlewares || {},
|
|
95
|
+
contentTypes: contentTypes || {},
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const loadIndex = async dir => {
|
|
100
|
+
if (await fse.pathExists(join(dir, 'index.js'))) {
|
|
101
|
+
return loadFile(join(dir, 'index.js'));
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const loadContentTypes = async dir => {
|
|
106
|
+
if (!(await fse.pathExists(dir))) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const fds = await fse.readdir(dir, { withFileTypes: true });
|
|
111
|
+
const contentTypes = {};
|
|
112
|
+
|
|
113
|
+
// only load folders
|
|
114
|
+
for (const fd of fds) {
|
|
115
|
+
if (fd.isFile()) {
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const contentTypeName = normalizeName(fd.name);
|
|
120
|
+
const contentType = await loadDir(join(dir, fd.name));
|
|
121
|
+
|
|
122
|
+
contentTypes[normalizeName(contentTypeName)] = _.defaults(contentType, DEFAULT_CONTENT_TYPE);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return contentTypes;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const loadDir = async dir => {
|
|
129
|
+
if (!(await fse.pathExists(dir))) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const fds = await fse.readdir(dir, { withFileTypes: true });
|
|
134
|
+
|
|
135
|
+
const root = {};
|
|
136
|
+
for (const fd of fds) {
|
|
137
|
+
if (!fd.isFile()) {
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const key = basename(fd.name, extname(fd.name));
|
|
142
|
+
root[normalizeName(key)] = await loadFile(join(dir, fd.name));
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return root;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const loadFile = file => {
|
|
149
|
+
const ext = extname(file);
|
|
150
|
+
|
|
151
|
+
switch (ext) {
|
|
152
|
+
case '.js':
|
|
153
|
+
return require(file);
|
|
154
|
+
case '.json':
|
|
155
|
+
return fse.readJSON(file);
|
|
156
|
+
default:
|
|
157
|
+
return {};
|
|
158
|
+
}
|
|
159
|
+
};
|
|
@@ -2,23 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
const { join } = require('path');
|
|
4
4
|
const _ = require('lodash');
|
|
5
|
-
const {
|
|
6
|
-
const loadFiles = require('
|
|
5
|
+
const { pathExists } = require('fs-extra');
|
|
6
|
+
const loadFiles = require('../../load/load-files');
|
|
7
7
|
|
|
8
8
|
module.exports = async strapi => {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
if (!(await exists(componentsDir))) {
|
|
9
|
+
if (!(await pathExists(strapi.dirs.components))) {
|
|
12
10
|
return {};
|
|
13
11
|
}
|
|
14
12
|
|
|
15
|
-
const map = await loadFiles(
|
|
13
|
+
const map = await loadFiles(strapi.dirs.components, '*/*.*(js|json)');
|
|
16
14
|
|
|
17
15
|
return Object.keys(map).reduce((acc, category) => {
|
|
18
16
|
Object.keys(map[category]).forEach(key => {
|
|
19
17
|
const schema = map[category][key];
|
|
20
18
|
|
|
21
|
-
const filePath = join(
|
|
19
|
+
const filePath = join(strapi.dirs.components, category, schema.__filename__);
|
|
22
20
|
|
|
23
21
|
if (!schema.collectionName) {
|
|
24
22
|
return strapi.stopWithError(
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
loadSrcIndex: require('./src-index'),
|
|
5
|
+
loadAPIs: require('./apis'),
|
|
6
|
+
loadMiddlewares: require('./middlewares'),
|
|
7
|
+
loadComponents: require('./components'),
|
|
8
|
+
loadPolicies: require('./policies'),
|
|
9
|
+
loadPlugins: require('./plugins'),
|
|
10
|
+
loadAdmin: require('./admin'),
|
|
11
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { join, extname, basename } = require('path');
|
|
4
|
+
const fse = require('fs-extra');
|
|
5
|
+
|
|
6
|
+
// TODO:: allow folders with index.js inside for bigger policies
|
|
7
|
+
module.exports = async function loadMiddlewares(strapi) {
|
|
8
|
+
const localMiddlewares = await loadLocalMiddlewares(strapi);
|
|
9
|
+
const internalMiddlewares = require('../../middlewares');
|
|
10
|
+
|
|
11
|
+
strapi.container.get('middlewares').add(`global::`, localMiddlewares);
|
|
12
|
+
strapi.container.get('middlewares').add(`strapi::`, internalMiddlewares);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const loadLocalMiddlewares = async strapi => {
|
|
16
|
+
const dir = strapi.dirs.middlewares;
|
|
17
|
+
|
|
18
|
+
if (!(await fse.pathExists(dir))) {
|
|
19
|
+
return {};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const middlewares = {};
|
|
23
|
+
const paths = await fse.readdir(dir, { withFileTypes: true });
|
|
24
|
+
|
|
25
|
+
for (const fd of paths) {
|
|
26
|
+
const { name } = fd;
|
|
27
|
+
const fullPath = join(dir, name);
|
|
28
|
+
|
|
29
|
+
if (fd.isFile() && extname(name) === '.js') {
|
|
30
|
+
const key = basename(name, '.js');
|
|
31
|
+
middlewares[key] = require(fullPath);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return middlewares;
|
|
36
|
+
};
|