@strapi/strapi 4.3.7 → 4.4.0-beta.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 +1 -1
- package/lib/Strapi.js +10 -2
- package/lib/commands/configurationDump.js +2 -2
- package/lib/core/loaders/apis.js +1 -1
- package/lib/core/loaders/plugins/get-enabled-plugins.js +3 -1
- package/lib/core/loaders/plugins/index.js +5 -5
- package/lib/core/registries/content-types.js +2 -2
- package/lib/core/registries/controllers.js +2 -2
- package/lib/core/registries/hooks.js +1 -1
- package/lib/core/registries/middlewares.js +2 -2
- package/lib/core/registries/policies.js +1 -1
- package/lib/core/registries/services.js +2 -2
- package/lib/core-api/controller/transform.js +1 -1
- package/lib/services/auth/index.js +4 -1
- package/lib/services/content-api/index.js +74 -0
- package/lib/services/content-api/permissions/engine.js +5 -0
- package/lib/services/content-api/permissions/index.js +148 -0
- package/lib/services/content-api/permissions/providers/action.js +19 -0
- package/lib/services/content-api/permissions/providers/condition.js +19 -0
- package/lib/services/content-api/permissions/providers/index.js +9 -0
- package/lib/services/cron.js +1 -1
- package/lib/services/entity-service/components.js +6 -6
- package/lib/services/entity-validator/index.js +22 -17
- package/lib/services/metrics/middleware.js +1 -1
- package/lib/services/server/register-routes.js +2 -2
- package/lib/services/utils/upload-files.js +1 -1
- package/lib/services/worker-queue.js +2 -2
- package/lib/types/core/strapi/index.d.ts +5 -0
- package/lib/utils/addSlash.js +6 -5
- package/package.json +15 -14
package/README.md
CHANGED
package/lib/Strapi.js
CHANGED
|
@@ -22,6 +22,7 @@ const createCronService = require('./services/cron');
|
|
|
22
22
|
const entityValidator = require('./services/entity-validator');
|
|
23
23
|
const createTelemetry = require('./services/metrics');
|
|
24
24
|
const createAuth = require('./services/auth');
|
|
25
|
+
const createContentAPI = require('./services/content-api');
|
|
25
26
|
const createUpdateNotifier = require('./utils/update-notifier');
|
|
26
27
|
const createStartupLogger = require('./utils/startup-logger');
|
|
27
28
|
const { LIFECYCLES } = require('./utils/lifecycles');
|
|
@@ -74,7 +75,7 @@ class Strapi {
|
|
|
74
75
|
// Load the app configuration from the dist directory
|
|
75
76
|
const appConfig = loadConfiguration({ appDir: rootDirs.app, distDir: rootDirs.dist }, opts);
|
|
76
77
|
|
|
77
|
-
//
|
|
78
|
+
// Instantiate the Strapi container
|
|
78
79
|
this.container = createContainer(this);
|
|
79
80
|
|
|
80
81
|
// Register every Strapi registry in the container
|
|
@@ -89,6 +90,7 @@ class Strapi {
|
|
|
89
90
|
this.container.register('plugins', pluginsRegistry(this));
|
|
90
91
|
this.container.register('apis', apisRegistry(this));
|
|
91
92
|
this.container.register('auth', createAuth(this));
|
|
93
|
+
this.container.register('content-api', createContentAPI(this));
|
|
92
94
|
this.container.register('sanitizers', sanitizersRegistry(this));
|
|
93
95
|
|
|
94
96
|
// Create a mapping of every useful directory (for the app, dist and static directories)
|
|
@@ -98,7 +100,7 @@ class Strapi {
|
|
|
98
100
|
this.isLoaded = false;
|
|
99
101
|
this.reload = this.reload();
|
|
100
102
|
|
|
101
|
-
//
|
|
103
|
+
// Instantiate the Koa app & the HTTP server
|
|
102
104
|
this.server = createServer(this);
|
|
103
105
|
|
|
104
106
|
// Strapi utils instanciation
|
|
@@ -188,6 +190,10 @@ class Strapi {
|
|
|
188
190
|
return this.container.get('auth');
|
|
189
191
|
}
|
|
190
192
|
|
|
193
|
+
get contentAPI() {
|
|
194
|
+
return this.container.get('content-api');
|
|
195
|
+
}
|
|
196
|
+
|
|
191
197
|
get sanitizers() {
|
|
192
198
|
return this.container.get('sanitizers');
|
|
193
199
|
}
|
|
@@ -445,6 +451,8 @@ class Strapi {
|
|
|
445
451
|
await this.server.initMiddlewares();
|
|
446
452
|
await this.server.initRouting();
|
|
447
453
|
|
|
454
|
+
await this.contentAPI.permissions.registerActions();
|
|
455
|
+
|
|
448
456
|
await this.runLifecyclesFunctions(LIFECYCLES.BOOTSTRAP);
|
|
449
457
|
|
|
450
458
|
this.cron.start();
|
|
@@ -9,7 +9,7 @@ const CHUNK_SIZE = 100;
|
|
|
9
9
|
* Will dump configurations to a file or stdout
|
|
10
10
|
* @param {string} file filepath to use as output
|
|
11
11
|
*/
|
|
12
|
-
module.exports = async
|
|
12
|
+
module.exports = async ({ file: filePath, pretty }) => {
|
|
13
13
|
const output = filePath ? fs.createWriteStream(filePath) : process.stdout;
|
|
14
14
|
|
|
15
15
|
const appContext = await strapi.compile();
|
|
@@ -21,7 +21,7 @@ module.exports = async function ({ file: filePath, pretty }) {
|
|
|
21
21
|
|
|
22
22
|
const pageCount = Math.ceil(count / CHUNK_SIZE);
|
|
23
23
|
|
|
24
|
-
for (let page = 0; page < pageCount; page
|
|
24
|
+
for (let page = 0; page < pageCount; page += 1) {
|
|
25
25
|
const results = await app
|
|
26
26
|
.query('strapi::core-store')
|
|
27
27
|
.findMany({ limit: CHUNK_SIZE, offset: page * CHUNK_SIZE, orderBy: 'key' });
|
package/lib/core/loaders/apis.js
CHANGED
|
@@ -59,7 +59,9 @@ const getEnabledPlugins = async (strapi) => {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
const installedPlugins = {};
|
|
62
|
-
|
|
62
|
+
const dependencies = strapi.config.get('info.dependencies', {});
|
|
63
|
+
|
|
64
|
+
for (const dep of Object.keys(dependencies)) {
|
|
63
65
|
const packagePath = join(dep, 'package.json');
|
|
64
66
|
let packageInfo;
|
|
65
67
|
try {
|
|
@@ -34,10 +34,10 @@ const applyUserExtension = async (plugins) => {
|
|
|
34
34
|
const extendedSchemas = await loadFiles(extensionsDir, '**/content-types/**/schema.json');
|
|
35
35
|
const strapiServers = await loadFiles(extensionsDir, '**/strapi-server.js');
|
|
36
36
|
|
|
37
|
-
for (const pluginName
|
|
37
|
+
for (const pluginName of Object.keys(plugins)) {
|
|
38
38
|
const plugin = plugins[pluginName];
|
|
39
39
|
// first: load json schema
|
|
40
|
-
for (const ctName
|
|
40
|
+
for (const ctName of Object.keys(plugin.contentTypes)) {
|
|
41
41
|
const extendedSchema = get([pluginName, 'content-types', ctName, 'schema'], extendedSchemas);
|
|
42
42
|
if (extendedSchema) {
|
|
43
43
|
plugin.contentTypes[ctName].schema = {
|
|
@@ -57,7 +57,7 @@ const applyUserExtension = async (plugins) => {
|
|
|
57
57
|
const applyUserConfig = async (plugins) => {
|
|
58
58
|
const userPluginsConfig = await getUserPluginsConfig();
|
|
59
59
|
|
|
60
|
-
for (const pluginName
|
|
60
|
+
for (const pluginName of Object.keys(plugins)) {
|
|
61
61
|
const plugin = plugins[pluginName];
|
|
62
62
|
const userPluginConfig = getOr({}, `${pluginName}.config`, userPluginsConfig);
|
|
63
63
|
const defaultConfig =
|
|
@@ -82,7 +82,7 @@ const loadPlugins = async (strapi) => {
|
|
|
82
82
|
|
|
83
83
|
strapi.config.set('enabledPlugins', enabledPlugins);
|
|
84
84
|
|
|
85
|
-
for (const pluginName
|
|
85
|
+
for (const pluginName of Object.keys(enabledPlugins)) {
|
|
86
86
|
const enabledPlugin = enabledPlugins[pluginName];
|
|
87
87
|
|
|
88
88
|
const serverEntrypointPath = join(enabledPlugin.pathToPlugin, 'strapi-server.js');
|
|
@@ -100,7 +100,7 @@ const loadPlugins = async (strapi) => {
|
|
|
100
100
|
await applyUserConfig(plugins);
|
|
101
101
|
await applyUserExtension(plugins);
|
|
102
102
|
|
|
103
|
-
for (const pluginName
|
|
103
|
+
for (const pluginName of Object.keys(plugins)) {
|
|
104
104
|
strapi.container.get('plugins').add(pluginName, plugins[pluginName]);
|
|
105
105
|
}
|
|
106
106
|
};
|
|
@@ -5,7 +5,7 @@ const { createContentType } = require('../domain/content-type');
|
|
|
5
5
|
const { addNamespace, hasNamespace } = require('../utils');
|
|
6
6
|
|
|
7
7
|
const validateKeySameToSingularName = (contentTypes) => {
|
|
8
|
-
for (const ctName
|
|
8
|
+
for (const ctName of Object.keys(contentTypes)) {
|
|
9
9
|
const contentType = contentTypes[ctName];
|
|
10
10
|
|
|
11
11
|
if (ctName !== contentType.schema.info.singularName) {
|
|
@@ -63,7 +63,7 @@ const contentTypesRegistry = () => {
|
|
|
63
63
|
add(namespace, newContentTypes) {
|
|
64
64
|
validateKeySameToSingularName(newContentTypes);
|
|
65
65
|
|
|
66
|
-
for (const rawCtName
|
|
66
|
+
for (const rawCtName of Object.keys(newContentTypes)) {
|
|
67
67
|
const uid = addNamespace(rawCtName, namespace);
|
|
68
68
|
|
|
69
69
|
if (has(uid, contentTypes)) {
|
|
@@ -48,7 +48,7 @@ const controllersRegistry = () => {
|
|
|
48
48
|
const filteredControllers = pickBy((_, uid) => hasNamespace(uid, namespace))(controllers);
|
|
49
49
|
|
|
50
50
|
const map = {};
|
|
51
|
-
for (const uid
|
|
51
|
+
for (const uid of Object.keys(filteredControllers)) {
|
|
52
52
|
Object.defineProperty(map, uid, {
|
|
53
53
|
enumerable: true,
|
|
54
54
|
get: () => {
|
|
@@ -78,7 +78,7 @@ const controllersRegistry = () => {
|
|
|
78
78
|
* @returns
|
|
79
79
|
*/
|
|
80
80
|
add(namespace, newControllers) {
|
|
81
|
-
for (const controllerName
|
|
81
|
+
for (const controllerName of Object.keys(newControllers)) {
|
|
82
82
|
const controller = newControllers[controllerName];
|
|
83
83
|
const uid = addNamespace(controllerName, namespace);
|
|
84
84
|
|
|
@@ -54,8 +54,8 @@ const middlewaresRegistry = () => {
|
|
|
54
54
|
* @param {{ [key: string]: Middleware }} newMiddlewares
|
|
55
55
|
* @returns
|
|
56
56
|
*/
|
|
57
|
-
add(namespace, rawMiddlewares) {
|
|
58
|
-
for (const middlewareName
|
|
57
|
+
add(namespace, rawMiddlewares = {}) {
|
|
58
|
+
for (const middlewareName of Object.keys(rawMiddlewares)) {
|
|
59
59
|
const middleware = rawMiddlewares[middlewareName];
|
|
60
60
|
const uid = addNamespace(middlewareName, namespace);
|
|
61
61
|
|
|
@@ -55,7 +55,7 @@ const policiesRegistry = () => {
|
|
|
55
55
|
* @returns
|
|
56
56
|
*/
|
|
57
57
|
add(namespace, newPolicies) {
|
|
58
|
-
for (const policyName
|
|
58
|
+
for (const policyName of Object.keys(newPolicies)) {
|
|
59
59
|
const policy = newPolicies[policyName];
|
|
60
60
|
const uid = addNamespace(policyName, namespace);
|
|
61
61
|
|
|
@@ -48,7 +48,7 @@ const servicesRegistry = (strapi) => {
|
|
|
48
48
|
|
|
49
49
|
// create lazy accessor to avoid instantiating the services;
|
|
50
50
|
const map = {};
|
|
51
|
-
for (const uid
|
|
51
|
+
for (const uid of Object.keys(filteredServices)) {
|
|
52
52
|
Object.defineProperty(map, uid, {
|
|
53
53
|
enumerable: true,
|
|
54
54
|
get: () => {
|
|
@@ -78,7 +78,7 @@ const servicesRegistry = (strapi) => {
|
|
|
78
78
|
* @returns
|
|
79
79
|
*/
|
|
80
80
|
add(namespace, newServices) {
|
|
81
|
-
for (const serviceName
|
|
81
|
+
for (const serviceName of Object.keys(newServices)) {
|
|
82
82
|
const service = newServices[serviceName];
|
|
83
83
|
const uid = addNamespace(serviceName, namespace);
|
|
84
84
|
|
|
@@ -32,6 +32,7 @@ const createAuthentication = () => {
|
|
|
32
32
|
|
|
33
33
|
return this;
|
|
34
34
|
},
|
|
35
|
+
|
|
35
36
|
async authenticate(ctx, next) {
|
|
36
37
|
const { route } = ctx.state;
|
|
37
38
|
|
|
@@ -47,7 +48,7 @@ const createAuthentication = () => {
|
|
|
47
48
|
for (const strategy of strategiesToUse) {
|
|
48
49
|
const result = await strategy.authenticate(ctx);
|
|
49
50
|
|
|
50
|
-
const { authenticated = false,
|
|
51
|
+
const { authenticated = false, credentials, ability = null, error = null } = result || {};
|
|
51
52
|
|
|
52
53
|
if (error !== null) {
|
|
53
54
|
return ctx.unauthorized(error);
|
|
@@ -58,6 +59,7 @@ const createAuthentication = () => {
|
|
|
58
59
|
ctx.state.auth = {
|
|
59
60
|
strategy,
|
|
60
61
|
credentials,
|
|
62
|
+
ability,
|
|
61
63
|
};
|
|
62
64
|
|
|
63
65
|
return next();
|
|
@@ -66,6 +68,7 @@ const createAuthentication = () => {
|
|
|
66
68
|
|
|
67
69
|
return ctx.unauthorized('Missing or invalid credentials');
|
|
68
70
|
},
|
|
71
|
+
|
|
69
72
|
async verify(auth, config = {}) {
|
|
70
73
|
if (config === false) {
|
|
71
74
|
return;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const _ = require('lodash');
|
|
4
|
+
const instantiatePermissionsUtilities = require('./permissions');
|
|
5
|
+
|
|
6
|
+
const transformRoutePrefixFor = (pluginName) => (route) => {
|
|
7
|
+
const prefix = route.config && route.config.prefix;
|
|
8
|
+
const path = prefix !== undefined ? `${prefix}${route.path}` : `/${pluginName}${route.path}`;
|
|
9
|
+
|
|
10
|
+
return {
|
|
11
|
+
...route,
|
|
12
|
+
path,
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Create a content API container that holds logic, tools and utils. (eg: permissions, ...)
|
|
18
|
+
*/
|
|
19
|
+
const createContentAPI = (strapi) => {
|
|
20
|
+
const getRoutesMap = async () => {
|
|
21
|
+
const routesMap = {};
|
|
22
|
+
|
|
23
|
+
_.forEach(strapi.api, (api, apiName) => {
|
|
24
|
+
const routes = _.flatMap(api.routes, (route) => {
|
|
25
|
+
if (_.has(route, 'routes')) {
|
|
26
|
+
return route.routes;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return route;
|
|
30
|
+
}).filter((route) => route.info.type === 'content-api');
|
|
31
|
+
|
|
32
|
+
if (routes.length === 0) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const apiPrefix = strapi.config.get('api.rest.prefix');
|
|
37
|
+
routesMap[`api::${apiName}`] = routes.map((route) => ({
|
|
38
|
+
...route,
|
|
39
|
+
path: `${apiPrefix}${route.path}`,
|
|
40
|
+
}));
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
_.forEach(strapi.plugins, (plugin, pluginName) => {
|
|
44
|
+
const transformPrefix = transformRoutePrefixFor(pluginName);
|
|
45
|
+
|
|
46
|
+
const routes = _.flatMap(plugin.routes, (route) => {
|
|
47
|
+
if (_.has(route, 'routes')) {
|
|
48
|
+
return route.routes.map(transformPrefix);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return transformPrefix(route);
|
|
52
|
+
}).filter((route) => route.info.type === 'content-api');
|
|
53
|
+
|
|
54
|
+
if (routes.length === 0) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const apiPrefix = strapi.config.get('api.rest.prefix');
|
|
59
|
+
routesMap[`plugin::${pluginName}`] = routes.map((route) => ({
|
|
60
|
+
...route,
|
|
61
|
+
path: `${apiPrefix}${route.path}`,
|
|
62
|
+
}));
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
return routesMap;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
permissions: instantiatePermissionsUtilities(strapi),
|
|
70
|
+
getRoutesMap,
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
module.exports = createContentAPI;
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const _ = require('lodash');
|
|
4
|
+
const { createActionProvider, createConditionProvider } = require('./providers');
|
|
5
|
+
const createPermissionEngine = require('./engine');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Creates an handler that checks if the permission's action exists in the action registry
|
|
9
|
+
*/
|
|
10
|
+
const createValidatePermissionHandler =
|
|
11
|
+
(actionProvider) =>
|
|
12
|
+
({ permission }) => {
|
|
13
|
+
const action = actionProvider.get(permission.action);
|
|
14
|
+
|
|
15
|
+
// If the action isn't registered into the action provider, then ignore the permission and warn the user
|
|
16
|
+
if (!action) {
|
|
17
|
+
strapi.log.debug(
|
|
18
|
+
`Unknown action "${permission.action}" supplied when registering a new permission`
|
|
19
|
+
);
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Create instances of providers and permission engine for the core content-API service.
|
|
26
|
+
* Also, expose utilities to get informations about available actions and such.
|
|
27
|
+
*
|
|
28
|
+
* @param {Strapi.Strapi} strapi
|
|
29
|
+
*/
|
|
30
|
+
module.exports = (strapi) => {
|
|
31
|
+
// NOTE: Here we define both an action and condition provider,
|
|
32
|
+
// but at the moment, we're only using the action one.
|
|
33
|
+
const providers = {
|
|
34
|
+
action: createActionProvider(),
|
|
35
|
+
condition: createConditionProvider(),
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Get a tree representation of the available Content API actions
|
|
40
|
+
* based on the methods of the Content API controllers.
|
|
41
|
+
*
|
|
42
|
+
* @note Only actions bound to a content-API route are returned.
|
|
43
|
+
*
|
|
44
|
+
* @return {{ [api: string]: { [controller: string]: string[] }}}
|
|
45
|
+
*/
|
|
46
|
+
const getActionsMap = () => {
|
|
47
|
+
const actionMap = {};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Check if a controller's action is bound to the
|
|
51
|
+
* content-api by looking at a potential __type__ symbol
|
|
52
|
+
*
|
|
53
|
+
* @param {object} action
|
|
54
|
+
*
|
|
55
|
+
* @return {boolean}
|
|
56
|
+
*/
|
|
57
|
+
const isContentApi = (action) => {
|
|
58
|
+
if (!_.has(action, Symbol.for('__type__'))) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return action[Symbol.for('__type__')].includes('content-api');
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Register actions from a specific API source into the result tree
|
|
67
|
+
*
|
|
68
|
+
* @param {{ [apiName]: { controllers: { [controller]: object } }}} apis The API container
|
|
69
|
+
* @param {string} source The prefix to use in front the API name
|
|
70
|
+
*
|
|
71
|
+
* @return {void}
|
|
72
|
+
*/
|
|
73
|
+
const registerAPIsActions = (apis, source) => {
|
|
74
|
+
_.forEach(apis, (api, apiName) => {
|
|
75
|
+
const controllers = _.reduce(
|
|
76
|
+
api.controllers,
|
|
77
|
+
(acc, controller, controllerName) => {
|
|
78
|
+
const contentApiActions = _.pickBy(controller, isContentApi);
|
|
79
|
+
|
|
80
|
+
if (_.isEmpty(contentApiActions)) {
|
|
81
|
+
return acc;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
acc[controllerName] = Object.keys(contentApiActions);
|
|
85
|
+
|
|
86
|
+
return acc;
|
|
87
|
+
},
|
|
88
|
+
{}
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
if (!_.isEmpty(controllers)) {
|
|
92
|
+
actionMap[`${source}::${apiName}`] = { controllers };
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
registerAPIsActions(strapi.api, 'api');
|
|
98
|
+
registerAPIsActions(strapi.plugins, 'plugin');
|
|
99
|
+
|
|
100
|
+
return actionMap;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Register all the content-API's controllers actions into the action provider.
|
|
105
|
+
* This method make use of the {@link getActionsMap} to generate the list of actions to register.
|
|
106
|
+
*
|
|
107
|
+
* @return {void}
|
|
108
|
+
*/
|
|
109
|
+
const registerActions = async () => {
|
|
110
|
+
const actionsMap = getActionsMap();
|
|
111
|
+
|
|
112
|
+
// For each API
|
|
113
|
+
for (const [api, value] of Object.entries(actionsMap)) {
|
|
114
|
+
const { controllers } = value;
|
|
115
|
+
|
|
116
|
+
// Register controllers methods as actions
|
|
117
|
+
for (const [controller, actions] of Object.entries(controllers)) {
|
|
118
|
+
// Register each action individually
|
|
119
|
+
await Promise.all(
|
|
120
|
+
actions.map((action) => {
|
|
121
|
+
const actionUID = `${api}.${controller}.${action}`;
|
|
122
|
+
|
|
123
|
+
return providers.action.register(actionUID, {
|
|
124
|
+
api,
|
|
125
|
+
controller,
|
|
126
|
+
action,
|
|
127
|
+
uid: actionUID,
|
|
128
|
+
});
|
|
129
|
+
})
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// Create an instance of a content-API permission engine
|
|
136
|
+
// and binds a custom validation handler to it
|
|
137
|
+
const engine = createPermissionEngine({ providers }).on(
|
|
138
|
+
'before-format::validate.permission',
|
|
139
|
+
createValidatePermissionHandler(providers.action)
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
engine,
|
|
144
|
+
providers,
|
|
145
|
+
registerActions,
|
|
146
|
+
getActionsMap,
|
|
147
|
+
};
|
|
148
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { providerFactory } = require('@strapi/utils');
|
|
4
|
+
|
|
5
|
+
module.exports = (options = {}) => {
|
|
6
|
+
const provider = providerFactory(options);
|
|
7
|
+
|
|
8
|
+
return {
|
|
9
|
+
...provider,
|
|
10
|
+
|
|
11
|
+
async register(action, payload) {
|
|
12
|
+
if (strapi.isLoaded) {
|
|
13
|
+
throw new Error(`You can't register new actions outside the bootstrap function.`);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return provider.register(action, payload);
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { providerFactory } = require('@strapi/utils');
|
|
4
|
+
|
|
5
|
+
module.exports = (options = {}) => {
|
|
6
|
+
const provider = providerFactory(options);
|
|
7
|
+
|
|
8
|
+
return {
|
|
9
|
+
...provider,
|
|
10
|
+
|
|
11
|
+
async register(condition) {
|
|
12
|
+
if (strapi.isLoaded) {
|
|
13
|
+
throw new Error(`You can't register new conditions outside the bootstrap function.`);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return provider.register(condition.name, condition);
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
};
|
package/lib/services/cron.js
CHANGED
|
@@ -18,11 +18,11 @@ const omitComponentData = (contentType, data) => {
|
|
|
18
18
|
|
|
19
19
|
// NOTE: we could generalize the logic to allow CRUD of relation directly in the DB layer
|
|
20
20
|
const createComponents = async (uid, data) => {
|
|
21
|
-
const { attributes } = strapi.getModel(uid);
|
|
21
|
+
const { attributes = {} } = strapi.getModel(uid);
|
|
22
22
|
|
|
23
23
|
const componentBody = {};
|
|
24
24
|
|
|
25
|
-
for (const attributeName
|
|
25
|
+
for (const attributeName of Object.keys(attributes)) {
|
|
26
26
|
const attribute = attributes[attributeName];
|
|
27
27
|
|
|
28
28
|
if (!has(attributeName, data) || !contentTypesUtils.isComponentAttribute(attribute)) {
|
|
@@ -118,11 +118,11 @@ const getComponents = async (uid, entity) => {
|
|
|
118
118
|
create or update
|
|
119
119
|
*/
|
|
120
120
|
const updateComponents = async (uid, entityToUpdate, data) => {
|
|
121
|
-
const { attributes } = strapi.getModel(uid);
|
|
121
|
+
const { attributes = {} } = strapi.getModel(uid);
|
|
122
122
|
|
|
123
123
|
const componentBody = {};
|
|
124
124
|
|
|
125
|
-
for (const attributeName
|
|
125
|
+
for (const attributeName of Object.keys(attributes)) {
|
|
126
126
|
const attribute = attributes[attributeName];
|
|
127
127
|
|
|
128
128
|
if (!has(attributeName, data)) {
|
|
@@ -275,9 +275,9 @@ const deleteOldDZComponents = async (uid, entityToUpdate, attributeName, dynamic
|
|
|
275
275
|
};
|
|
276
276
|
|
|
277
277
|
const deleteComponents = async (uid, entityToDelete) => {
|
|
278
|
-
const { attributes } = strapi.getModel(uid);
|
|
278
|
+
const { attributes = {} } = strapi.getModel(uid);
|
|
279
279
|
|
|
280
|
-
for (const attributeName
|
|
280
|
+
for (const attributeName of Object.keys(attributes)) {
|
|
281
281
|
const attribute = attributes[attributeName];
|
|
282
282
|
|
|
283
283
|
if (attribute.type === 'component') {
|
|
@@ -14,51 +14,56 @@ const { isMediaAttribute, isScalarAttribute, getWritableAttributes } = strapiUti
|
|
|
14
14
|
const { ValidationError } = strapiUtils.errors;
|
|
15
15
|
|
|
16
16
|
const addMinMax = (validator, { attr, updatedAttribute }) => {
|
|
17
|
+
let nextValidator = validator;
|
|
18
|
+
|
|
17
19
|
if (
|
|
18
20
|
Number.isInteger(attr.min) &&
|
|
19
21
|
(attr.required || (Array.isArray(updatedAttribute.value) && updatedAttribute.value.length > 0))
|
|
20
22
|
) {
|
|
21
|
-
|
|
23
|
+
nextValidator = nextValidator.min(attr.min);
|
|
22
24
|
}
|
|
23
25
|
if (Number.isInteger(attr.max)) {
|
|
24
|
-
|
|
26
|
+
nextValidator = nextValidator.max(attr.max);
|
|
25
27
|
}
|
|
26
|
-
return
|
|
28
|
+
return nextValidator;
|
|
27
29
|
};
|
|
28
30
|
|
|
29
|
-
const addRequiredValidation =
|
|
30
|
-
(
|
|
31
|
-
|
|
31
|
+
const addRequiredValidation = (createOrUpdate) => {
|
|
32
|
+
return (validator, { attr: { required } }) => {
|
|
33
|
+
let nextValidator = validator;
|
|
32
34
|
if (required) {
|
|
33
35
|
if (createOrUpdate === 'creation') {
|
|
34
|
-
|
|
36
|
+
nextValidator = nextValidator.notNil();
|
|
35
37
|
} else if (createOrUpdate === 'update') {
|
|
36
|
-
|
|
38
|
+
nextValidator = nextValidator.notNull();
|
|
37
39
|
}
|
|
38
40
|
} else {
|
|
39
|
-
|
|
41
|
+
nextValidator = nextValidator.nullable();
|
|
40
42
|
}
|
|
41
|
-
return
|
|
43
|
+
return nextValidator;
|
|
42
44
|
};
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const addDefault = (createOrUpdate) => {
|
|
48
|
+
return (validator, { attr }) => {
|
|
49
|
+
let nextValidator = validator;
|
|
43
50
|
|
|
44
|
-
const addDefault =
|
|
45
|
-
(createOrUpdate) =>
|
|
46
|
-
(validator, { attr }) => {
|
|
47
51
|
if (createOrUpdate === 'creation') {
|
|
48
52
|
if (
|
|
49
53
|
((attr.type === 'component' && attr.repeatable) || attr.type === 'dynamiczone') &&
|
|
50
54
|
!attr.required
|
|
51
55
|
) {
|
|
52
|
-
|
|
56
|
+
nextValidator = nextValidator.default([]);
|
|
53
57
|
} else {
|
|
54
|
-
|
|
58
|
+
nextValidator = nextValidator.default(attr.default);
|
|
55
59
|
}
|
|
56
60
|
} else {
|
|
57
|
-
|
|
61
|
+
nextValidator = nextValidator.default(undefined);
|
|
58
62
|
}
|
|
59
63
|
|
|
60
|
-
return
|
|
64
|
+
return nextValidator;
|
|
61
65
|
};
|
|
66
|
+
};
|
|
62
67
|
|
|
63
68
|
const preventCast = (validator) => validator.transform((val, originalVal) => originalVal);
|
|
64
69
|
|
|
@@ -50,7 +50,7 @@ const registerAdminRoutes = (strapi) => {
|
|
|
50
50
|
* @param {import('../../').Strapi} strapi
|
|
51
51
|
*/
|
|
52
52
|
const registerPluginRoutes = (strapi) => {
|
|
53
|
-
for (const pluginName
|
|
53
|
+
for (const pluginName of Object.keys(strapi.plugins)) {
|
|
54
54
|
const plugin = strapi.plugins[pluginName];
|
|
55
55
|
|
|
56
56
|
const generateRouteScope = createRouteScopeGenerator(`plugin::${pluginName}`);
|
|
@@ -86,7 +86,7 @@ const registerPluginRoutes = (strapi) => {
|
|
|
86
86
|
* @param {import('../../').Strapi} strapi
|
|
87
87
|
*/
|
|
88
88
|
const registerAPIRoutes = (strapi) => {
|
|
89
|
-
for (const apiName
|
|
89
|
+
for (const apiName of Object.keys(strapi.api)) {
|
|
90
90
|
const api = strapi.api[apiName];
|
|
91
91
|
|
|
92
92
|
const generateRouteScope = createRouteScopeGenerator(`api::${apiName}`);
|
|
@@ -27,7 +27,7 @@ module.exports = async (uid, entity, files) => {
|
|
|
27
27
|
let tmpModel = modelDef;
|
|
28
28
|
let modelUID = uid;
|
|
29
29
|
|
|
30
|
-
for (let i = 0; i < path.length; i
|
|
30
|
+
for (let i = 0; i < path.length; i += 1) {
|
|
31
31
|
if (!tmpModel) return {};
|
|
32
32
|
const part = path[i];
|
|
33
33
|
const attr = tmpModel.attributes[part];
|
|
@@ -26,7 +26,7 @@ module.exports = class WorkerQueue {
|
|
|
26
26
|
enqueue(payload) {
|
|
27
27
|
debug('Enqueue event in worker queue');
|
|
28
28
|
if (this.running < this.concurrency) {
|
|
29
|
-
this.running
|
|
29
|
+
this.running += 1;
|
|
30
30
|
this.execute(payload);
|
|
31
31
|
} else {
|
|
32
32
|
this.queue.unshift(payload);
|
|
@@ -40,7 +40,7 @@ module.exports = class WorkerQueue {
|
|
|
40
40
|
if (payload) {
|
|
41
41
|
this.execute(payload);
|
|
42
42
|
} else {
|
|
43
|
-
this.running
|
|
43
|
+
this.running -= 1;
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
|
package/lib/utils/addSlash.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
module.exports = (path) => {
|
|
4
|
-
|
|
5
|
-
if (
|
|
4
|
+
let tmpPath = path;
|
|
5
|
+
if (typeof tmpPath !== 'string') throw new Error('admin.url must be a string');
|
|
6
|
+
if (tmpPath === '' || tmpPath === '/') return '/';
|
|
6
7
|
|
|
7
|
-
if (
|
|
8
|
-
if (
|
|
9
|
-
return
|
|
8
|
+
if (tmpPath[0] !== '/') tmpPath = `/${tmpPath}`;
|
|
9
|
+
if (tmpPath[tmpPath.length - 1] !== '/') tmpPath += '/';
|
|
10
|
+
return tmpPath;
|
|
10
11
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@strapi/strapi",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.4.0-beta.1",
|
|
4
4
|
"description": "An open source headless CMS solution to create and manage your own API. It provides a powerful dashboard and features to make your life easier. Databases supported: MySQL, MariaDB, PostgreSQL, SQLite",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"strapi",
|
|
@@ -80,17 +80,18 @@
|
|
|
80
80
|
"dependencies": {
|
|
81
81
|
"@koa/cors": "3.4.1",
|
|
82
82
|
"@koa/router": "10.1.1",
|
|
83
|
-
"@strapi/admin": "4.
|
|
84
|
-
"@strapi/database": "4.
|
|
85
|
-
"@strapi/generate-new": "4.
|
|
86
|
-
"@strapi/generators": "4.
|
|
87
|
-
"@strapi/logger": "4.
|
|
88
|
-
"@strapi/
|
|
89
|
-
"@strapi/plugin-content-
|
|
90
|
-
"@strapi/plugin-
|
|
91
|
-
"@strapi/plugin-
|
|
92
|
-
"@strapi/
|
|
93
|
-
"@strapi/utils": "4.
|
|
83
|
+
"@strapi/admin": "4.4.0-beta.1",
|
|
84
|
+
"@strapi/database": "4.4.0-beta.1",
|
|
85
|
+
"@strapi/generate-new": "4.4.0-beta.1",
|
|
86
|
+
"@strapi/generators": "4.4.0-beta.1",
|
|
87
|
+
"@strapi/logger": "4.4.0-beta.1",
|
|
88
|
+
"@strapi/permissions": "4.4.0-beta.1",
|
|
89
|
+
"@strapi/plugin-content-manager": "4.4.0-beta.1",
|
|
90
|
+
"@strapi/plugin-content-type-builder": "4.4.0-beta.1",
|
|
91
|
+
"@strapi/plugin-email": "4.4.0-beta.1",
|
|
92
|
+
"@strapi/plugin-upload": "4.4.0-beta.1",
|
|
93
|
+
"@strapi/typescript-utils": "4.4.0-beta.1",
|
|
94
|
+
"@strapi/utils": "4.4.0-beta.1",
|
|
94
95
|
"bcryptjs": "2.4.3",
|
|
95
96
|
"boxen": "5.1.2",
|
|
96
97
|
"chalk": "4.1.2",
|
|
@@ -136,8 +137,8 @@
|
|
|
136
137
|
"typescript": "4.6.2"
|
|
137
138
|
},
|
|
138
139
|
"engines": {
|
|
139
|
-
"node": ">=14.19.1 <=
|
|
140
|
+
"node": ">=14.19.1 <=18.x.x",
|
|
140
141
|
"npm": ">=6.0.0"
|
|
141
142
|
},
|
|
142
|
-
"gitHead": "
|
|
143
|
+
"gitHead": "cae16f7f259fa4473a55e8fea57839cda98f34ae"
|
|
143
144
|
}
|