@strapi/strapi 4.4.0-rc.1 → 4.5.0-alpha.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/lib/Strapi.js +2 -18
- package/lib/services/auth/index.js +1 -4
- package/lib/services/entity-service/components.js +4 -10
- package/lib/services/entity-service/index.js +4 -10
- package/lib/services/entity-service/params.js +1 -87
- package/lib/types/core/attributes/common.d.ts +0 -3
- package/lib/types/core/strapi/index.d.ts +7 -41
- package/package.json +13 -14
- package/lib/core/registries/custom-fields.js +0 -54
- package/lib/services/content-api/index.js +0 -74
- package/lib/services/content-api/permissions/engine.js +0 -5
- package/lib/services/content-api/permissions/index.js +0 -148
- package/lib/services/content-api/permissions/providers/action.js +0 -19
- package/lib/services/content-api/permissions/providers/condition.js +0 -19
- package/lib/services/content-api/permissions/providers/index.js +0 -9
- package/lib/services/custom-fields.js +0 -11
- package/lib/utils/convert-custom-field-type.js +0 -22
package/lib/Strapi.js
CHANGED
|
@@ -23,8 +23,6 @@ const entityValidator = require('./services/entity-validator');
|
|
|
23
23
|
const createTelemetry = require('./services/metrics');
|
|
24
24
|
const requestContext = require('./services/request-context');
|
|
25
25
|
const createAuth = require('./services/auth');
|
|
26
|
-
const createCustomFields = require('./services/custom-fields');
|
|
27
|
-
const createContentAPI = require('./services/content-api');
|
|
28
26
|
const createUpdateNotifier = require('./utils/update-notifier');
|
|
29
27
|
const createStartupLogger = require('./utils/startup-logger');
|
|
30
28
|
const { LIFECYCLES } = require('./utils/lifecycles');
|
|
@@ -37,14 +35,12 @@ const hooksRegistry = require('./core/registries/hooks');
|
|
|
37
35
|
const controllersRegistry = require('./core/registries/controllers');
|
|
38
36
|
const modulesRegistry = require('./core/registries/modules');
|
|
39
37
|
const pluginsRegistry = require('./core/registries/plugins');
|
|
40
|
-
const customFieldsRegistry = require('./core/registries/custom-fields');
|
|
41
38
|
const createConfigProvider = require('./core/registries/config');
|
|
42
39
|
const apisRegistry = require('./core/registries/apis');
|
|
43
40
|
const bootstrap = require('./core/bootstrap');
|
|
44
41
|
const loaders = require('./core/loaders');
|
|
45
42
|
const { destroyOnSignal } = require('./utils/signals');
|
|
46
43
|
const sanitizersRegistry = require('./core/registries/sanitizers');
|
|
47
|
-
const convertCustomFieldType = require('./utils/convert-custom-field-type');
|
|
48
44
|
|
|
49
45
|
// TODO: move somewhere else
|
|
50
46
|
const draftAndPublishSync = require('./migrations/draft-publish');
|
|
@@ -79,7 +75,7 @@ class Strapi {
|
|
|
79
75
|
// Load the app configuration from the dist directory
|
|
80
76
|
const appConfig = loadConfiguration({ appDir: rootDirs.app, distDir: rootDirs.dist }, opts);
|
|
81
77
|
|
|
82
|
-
//
|
|
78
|
+
// Instanciate the Strapi container
|
|
83
79
|
this.container = createContainer(this);
|
|
84
80
|
|
|
85
81
|
// Register every Strapi registry in the container
|
|
@@ -92,10 +88,8 @@ class Strapi {
|
|
|
92
88
|
this.container.register('controllers', controllersRegistry(this));
|
|
93
89
|
this.container.register('modules', modulesRegistry(this));
|
|
94
90
|
this.container.register('plugins', pluginsRegistry(this));
|
|
95
|
-
this.container.register('custom-fields', customFieldsRegistry(this));
|
|
96
91
|
this.container.register('apis', apisRegistry(this));
|
|
97
92
|
this.container.register('auth', createAuth(this));
|
|
98
|
-
this.container.register('content-api', createContentAPI(this));
|
|
99
93
|
this.container.register('sanitizers', sanitizersRegistry(this));
|
|
100
94
|
|
|
101
95
|
// Create a mapping of every useful directory (for the app, dist and static directories)
|
|
@@ -105,7 +99,7 @@ class Strapi {
|
|
|
105
99
|
this.isLoaded = false;
|
|
106
100
|
this.reload = this.reload();
|
|
107
101
|
|
|
108
|
-
//
|
|
102
|
+
// Instanciate the Koa app & the HTTP server
|
|
109
103
|
this.server = createServer(this);
|
|
110
104
|
|
|
111
105
|
// Strapi utils instanciation
|
|
@@ -117,8 +111,6 @@ class Strapi {
|
|
|
117
111
|
this.telemetry = createTelemetry(this);
|
|
118
112
|
this.requestContext = requestContext;
|
|
119
113
|
|
|
120
|
-
this.customFields = createCustomFields(this);
|
|
121
|
-
|
|
122
114
|
createUpdateNotifier(this).notify();
|
|
123
115
|
}
|
|
124
116
|
|
|
@@ -198,10 +190,6 @@ class Strapi {
|
|
|
198
190
|
return this.container.get('auth');
|
|
199
191
|
}
|
|
200
192
|
|
|
201
|
-
get contentAPI() {
|
|
202
|
-
return this.container.get('content-api');
|
|
203
|
-
}
|
|
204
|
-
|
|
205
193
|
get sanitizers() {
|
|
206
194
|
return this.container.get('sanitizers');
|
|
207
195
|
}
|
|
@@ -391,8 +379,6 @@ class Strapi {
|
|
|
391
379
|
this.telemetry.register();
|
|
392
380
|
|
|
393
381
|
await this.runLifecyclesFunctions(LIFECYCLES.REGISTER);
|
|
394
|
-
// NOTE: Swap type customField for underlying data type
|
|
395
|
-
convertCustomFieldType(this);
|
|
396
382
|
|
|
397
383
|
return this;
|
|
398
384
|
}
|
|
@@ -461,8 +447,6 @@ class Strapi {
|
|
|
461
447
|
await this.server.initMiddlewares();
|
|
462
448
|
await this.server.initRouting();
|
|
463
449
|
|
|
464
|
-
await this.contentAPI.permissions.registerActions();
|
|
465
|
-
|
|
466
450
|
await this.runLifecyclesFunctions(LIFECYCLES.BOOTSTRAP);
|
|
467
451
|
|
|
468
452
|
this.cron.start();
|
|
@@ -32,7 +32,6 @@ const createAuthentication = () => {
|
|
|
32
32
|
|
|
33
33
|
return this;
|
|
34
34
|
},
|
|
35
|
-
|
|
36
35
|
async authenticate(ctx, next) {
|
|
37
36
|
const { route } = ctx.state;
|
|
38
37
|
|
|
@@ -48,7 +47,7 @@ const createAuthentication = () => {
|
|
|
48
47
|
for (const strategy of strategiesToUse) {
|
|
49
48
|
const result = await strategy.authenticate(ctx);
|
|
50
49
|
|
|
51
|
-
const { authenticated = false,
|
|
50
|
+
const { authenticated = false, error = null, credentials } = result || {};
|
|
52
51
|
|
|
53
52
|
if (error !== null) {
|
|
54
53
|
return ctx.unauthorized(error);
|
|
@@ -59,7 +58,6 @@ const createAuthentication = () => {
|
|
|
59
58
|
ctx.state.auth = {
|
|
60
59
|
strategy,
|
|
61
60
|
credentials,
|
|
62
|
-
ability,
|
|
63
61
|
};
|
|
64
62
|
|
|
65
63
|
return next();
|
|
@@ -68,7 +66,6 @@ const createAuthentication = () => {
|
|
|
68
66
|
|
|
69
67
|
return ctx.unauthorized('Missing or invalid credentials');
|
|
70
68
|
},
|
|
71
|
-
|
|
72
69
|
async verify(auth, config = {}) {
|
|
73
70
|
if (config === false) {
|
|
74
71
|
return;
|
|
@@ -48,11 +48,10 @@ const createComponents = async (uid, data) => {
|
|
|
48
48
|
);
|
|
49
49
|
|
|
50
50
|
// TODO: add order
|
|
51
|
-
componentBody[attributeName] = components.map(({ id }
|
|
51
|
+
componentBody[attributeName] = components.map(({ id }) => {
|
|
52
52
|
return {
|
|
53
53
|
id,
|
|
54
54
|
__pivot: {
|
|
55
|
-
order: idx + 1,
|
|
56
55
|
field: attributeName,
|
|
57
56
|
component_type: componentUID,
|
|
58
57
|
},
|
|
@@ -63,7 +62,6 @@ const createComponents = async (uid, data) => {
|
|
|
63
62
|
componentBody[attributeName] = {
|
|
64
63
|
id: component.id,
|
|
65
64
|
__pivot: {
|
|
66
|
-
order: 1,
|
|
67
65
|
field: attributeName,
|
|
68
66
|
component_type: componentUID,
|
|
69
67
|
},
|
|
@@ -81,13 +79,12 @@ const createComponents = async (uid, data) => {
|
|
|
81
79
|
}
|
|
82
80
|
|
|
83
81
|
componentBody[attributeName] = await Promise.all(
|
|
84
|
-
dynamiczoneValues.map(async (value
|
|
82
|
+
dynamiczoneValues.map(async (value) => {
|
|
85
83
|
const { id } = await createComponent(value.__component, value);
|
|
86
84
|
return {
|
|
87
85
|
id,
|
|
88
86
|
__component: value.__component,
|
|
89
87
|
__pivot: {
|
|
90
|
-
order: idx + 1,
|
|
91
88
|
field: attributeName,
|
|
92
89
|
},
|
|
93
90
|
};
|
|
@@ -145,11 +142,10 @@ const updateComponents = async (uid, entityToUpdate, data) => {
|
|
|
145
142
|
componentValue.map((value) => updateOrCreateComponent(componentUID, value))
|
|
146
143
|
);
|
|
147
144
|
|
|
148
|
-
componentBody[attributeName] = components.filter(_.negate(_.isNil)).map(({ id }
|
|
145
|
+
componentBody[attributeName] = components.filter(_.negate(_.isNil)).map(({ id }) => {
|
|
149
146
|
return {
|
|
150
147
|
id,
|
|
151
148
|
__pivot: {
|
|
152
|
-
order: idx + 1,
|
|
153
149
|
field: attributeName,
|
|
154
150
|
component_type: componentUID,
|
|
155
151
|
},
|
|
@@ -160,7 +156,6 @@ const updateComponents = async (uid, entityToUpdate, data) => {
|
|
|
160
156
|
componentBody[attributeName] = component && {
|
|
161
157
|
id: component.id,
|
|
162
158
|
__pivot: {
|
|
163
|
-
order: 1,
|
|
164
159
|
field: attributeName,
|
|
165
160
|
component_type: componentUID,
|
|
166
161
|
},
|
|
@@ -180,14 +175,13 @@ const updateComponents = async (uid, entityToUpdate, data) => {
|
|
|
180
175
|
}
|
|
181
176
|
|
|
182
177
|
componentBody[attributeName] = await Promise.all(
|
|
183
|
-
dynamiczoneValues.map(async (value
|
|
178
|
+
dynamiczoneValues.map(async (value) => {
|
|
184
179
|
const { id } = await updateOrCreateComponent(value.__component, value);
|
|
185
180
|
|
|
186
181
|
return {
|
|
187
182
|
id,
|
|
188
183
|
__component: value.__component,
|
|
189
184
|
__pivot: {
|
|
190
|
-
order: idx + 1,
|
|
191
185
|
field: attributeName,
|
|
192
186
|
},
|
|
193
187
|
};
|
|
@@ -10,6 +10,7 @@ const {
|
|
|
10
10
|
sanitize,
|
|
11
11
|
} = require('@strapi/utils');
|
|
12
12
|
const { ValidationError } = require('@strapi/utils').errors;
|
|
13
|
+
const { transformParamsToQuery } = require('@strapi/utils').convertQueryParams;
|
|
13
14
|
const uploadFiles = require('../utils/upload-files');
|
|
14
15
|
|
|
15
16
|
const {
|
|
@@ -19,7 +20,7 @@ const {
|
|
|
19
20
|
updateComponents,
|
|
20
21
|
deleteComponents,
|
|
21
22
|
} = require('./components');
|
|
22
|
-
const {
|
|
23
|
+
const { pickSelectionParams } = require('./params');
|
|
23
24
|
const { applyTransforms } = require('./attributes');
|
|
24
25
|
|
|
25
26
|
// TODO: those should be strapi events used by the webhooks not the other way arround
|
|
@@ -83,12 +84,7 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator })
|
|
|
83
84
|
|
|
84
85
|
const query = transformParamsToQuery(uid, wrappedParams);
|
|
85
86
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
return {
|
|
89
|
-
results,
|
|
90
|
-
pagination,
|
|
91
|
-
};
|
|
87
|
+
return db.query(uid).findPage(query);
|
|
92
88
|
},
|
|
93
89
|
|
|
94
90
|
async findWithRelationCounts(uid, opts) {
|
|
@@ -96,9 +92,7 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator })
|
|
|
96
92
|
|
|
97
93
|
const query = transformParamsToQuery(uid, wrappedParams);
|
|
98
94
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
return results;
|
|
95
|
+
return db.query(uid).findMany(query);
|
|
102
96
|
},
|
|
103
97
|
|
|
104
98
|
async findOne(uid, entityId, opts) {
|
|
@@ -1,95 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { pick
|
|
4
|
-
const { PaginationError } = require('@strapi/utils').errors;
|
|
5
|
-
|
|
6
|
-
const {
|
|
7
|
-
convertSortQueryParams,
|
|
8
|
-
convertLimitQueryParams,
|
|
9
|
-
convertStartQueryParams,
|
|
10
|
-
convertPopulateQueryParams,
|
|
11
|
-
convertFiltersQueryParams,
|
|
12
|
-
convertFieldsQueryParams,
|
|
13
|
-
convertPublicationStateParams,
|
|
14
|
-
} = require('@strapi/utils/lib/convert-query-params');
|
|
3
|
+
const { pick } = require('lodash/fp');
|
|
15
4
|
|
|
16
5
|
const pickSelectionParams = pick(['fields', 'populate']);
|
|
17
6
|
|
|
18
|
-
const transformParamsToQuery = (uid, params) => {
|
|
19
|
-
// NOTE: can be a CT, a Compo or nothing in the case of polymorphism (DZ & morph relations)
|
|
20
|
-
const schema = strapi.getModel(uid);
|
|
21
|
-
|
|
22
|
-
const query = {};
|
|
23
|
-
|
|
24
|
-
const { _q, sort, filters, fields, populate, page, pageSize, start, limit } = params;
|
|
25
|
-
|
|
26
|
-
if (!isNil(_q)) {
|
|
27
|
-
query._q = _q;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (!isNil(sort)) {
|
|
31
|
-
query.orderBy = convertSortQueryParams(sort);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
if (!isNil(filters)) {
|
|
35
|
-
query.where = convertFiltersQueryParams(filters, schema);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (!isNil(fields)) {
|
|
39
|
-
query.select = convertFieldsQueryParams(fields);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (!isNil(populate)) {
|
|
43
|
-
query.populate = convertPopulateQueryParams(populate, schema);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const isPagePagination = !isNil(page) || !isNil(pageSize);
|
|
47
|
-
const isOffsetPagination = !isNil(start) || !isNil(limit);
|
|
48
|
-
|
|
49
|
-
if (isPagePagination && isOffsetPagination) {
|
|
50
|
-
throw new PaginationError(
|
|
51
|
-
'Invalid pagination attributes. You cannot use page and offset pagination in the same query'
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (!isNil(page)) {
|
|
56
|
-
const pageVal = toNumber(page);
|
|
57
|
-
|
|
58
|
-
if (!isInteger(pageVal) || pageVal <= 0) {
|
|
59
|
-
throw new PaginationError(
|
|
60
|
-
`Invalid 'page' parameter. Expected an integer > 0, received: ${page}`
|
|
61
|
-
);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
query.page = pageVal;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (!isNil(pageSize)) {
|
|
68
|
-
const pageSizeVal = toNumber(pageSize);
|
|
69
|
-
|
|
70
|
-
if (!isInteger(pageSizeVal) || pageSizeVal <= 0) {
|
|
71
|
-
throw new PaginationError(
|
|
72
|
-
`Invalid 'pageSize' parameter. Expected an integer > 0, received: ${page}`
|
|
73
|
-
);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
query.pageSize = pageSizeVal;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (!isNil(start)) {
|
|
80
|
-
query.offset = convertStartQueryParams(start);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (!isNil(limit)) {
|
|
84
|
-
query.limit = convertLimitQueryParams(limit);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
convertPublicationStateParams(schema, params, query);
|
|
88
|
-
|
|
89
|
-
return query;
|
|
90
|
-
};
|
|
91
|
-
|
|
92
7
|
module.exports = {
|
|
93
|
-
transformParamsToQuery,
|
|
94
8
|
pickSelectionParams,
|
|
95
9
|
};
|
|
@@ -29,9 +29,6 @@ export type NonUniqueAttribute = { unique: false };
|
|
|
29
29
|
export type ConfigurableAttribute = { configurable: true };
|
|
30
30
|
export type NonConfigurableAttribute = { configurable: false };
|
|
31
31
|
|
|
32
|
-
// custom field
|
|
33
|
-
export type CustomField<T extends string, P extends object = undefined> = { customField: T, options?: P };
|
|
34
|
-
|
|
35
32
|
// min/max
|
|
36
33
|
export type SetMinMax<T extends MinMaxOption<U>, U = number> = T;
|
|
37
34
|
|
|
@@ -5,28 +5,6 @@ import type { StringMap } from './utils';
|
|
|
5
5
|
import type { GenericController } from '../../../core-api/controller'
|
|
6
6
|
import type { GenericService } from '../../../core-api/service'
|
|
7
7
|
|
|
8
|
-
// TODO move custom fields types to a separate file
|
|
9
|
-
interface CustomFieldServerOptions {
|
|
10
|
-
/**
|
|
11
|
-
* The name of the custom field
|
|
12
|
-
*/
|
|
13
|
-
name: string;
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* The name of the plugin creating the custom field
|
|
17
|
-
*/
|
|
18
|
-
plugin?: string;
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* The existing Strapi data type the custom field uses
|
|
22
|
-
*/
|
|
23
|
-
type: string;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
interface CustomFields {
|
|
27
|
-
register: (customFields: CustomFieldServerOptions[] | CustomFieldServerOptions) => void;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
8
|
/**
|
|
31
9
|
* The Strapi interface implemented by the main Strapi class.
|
|
32
10
|
*/
|
|
@@ -46,11 +24,6 @@ export interface Strapi {
|
|
|
46
24
|
*/
|
|
47
25
|
readonly auth: any;
|
|
48
26
|
|
|
49
|
-
/**
|
|
50
|
-
* Getter for the Strapi content API container
|
|
51
|
-
*/
|
|
52
|
-
readonly contentAPI: any;
|
|
53
|
-
|
|
54
27
|
/**
|
|
55
28
|
* Getter for the Strapi sanitizers container
|
|
56
29
|
*/
|
|
@@ -92,13 +65,6 @@ export interface Strapi {
|
|
|
92
65
|
*/
|
|
93
66
|
contentType(uid: string): any;
|
|
94
67
|
|
|
95
|
-
/**
|
|
96
|
-
* The custom fields registry
|
|
97
|
-
*
|
|
98
|
-
* It returns the custom fields interface
|
|
99
|
-
*/
|
|
100
|
-
readonly customFields: CustomFields;
|
|
101
|
-
|
|
102
68
|
/**
|
|
103
69
|
* Getter for the Strapi policies container
|
|
104
70
|
*
|
|
@@ -229,7 +195,7 @@ export interface Strapi {
|
|
|
229
195
|
/**
|
|
230
196
|
* Restart the server and reload all the configuration.
|
|
231
197
|
* It re-runs all the lifecycles phases.
|
|
232
|
-
*
|
|
198
|
+
*
|
|
233
199
|
* @example
|
|
234
200
|
* ``` ts
|
|
235
201
|
* setImmediate(() => strapi.reload());
|
|
@@ -257,13 +223,13 @@ export interface Strapi {
|
|
|
257
223
|
/**
|
|
258
224
|
* Opent he administration panel in a browser if the option is enabled.
|
|
259
225
|
* You can disable it using the admin.autoOpen configuration variable.
|
|
260
|
-
*
|
|
226
|
+
*
|
|
261
227
|
* Note: It only works in development envs.
|
|
262
228
|
*/
|
|
263
229
|
openAdmin(options: { isInitialized: boolean }): Promise<void>;
|
|
264
230
|
|
|
265
231
|
/**
|
|
266
|
-
* Load the admin panel server logic into the server code and initialize its configuration.
|
|
232
|
+
* Load the admin panel server logic into the server code and initialize its configuration.
|
|
267
233
|
*/
|
|
268
234
|
loadAdmin(): Promise<void>;
|
|
269
235
|
|
|
@@ -322,7 +288,7 @@ export interface Strapi {
|
|
|
322
288
|
container: any;
|
|
323
289
|
|
|
324
290
|
/**
|
|
325
|
-
* References to all the directories handled by Strapi
|
|
291
|
+
* References to all the directories handled by Strapi
|
|
326
292
|
*/
|
|
327
293
|
dirs: StrapiDirectories;
|
|
328
294
|
|
|
@@ -357,7 +323,7 @@ export interface Strapi {
|
|
|
357
323
|
startupLogger: any;
|
|
358
324
|
|
|
359
325
|
/**
|
|
360
|
-
* Strapi logger used to send errors, warning or information messages
|
|
326
|
+
* Strapi logger used to send errors, warning or information messages
|
|
361
327
|
*/
|
|
362
328
|
log: any;
|
|
363
329
|
|
|
@@ -390,7 +356,7 @@ export interface Strapi {
|
|
|
390
356
|
/**
|
|
391
357
|
* Entity Service instance
|
|
392
358
|
*/
|
|
393
|
-
entityService: any;
|
|
359
|
+
entityService: any;
|
|
394
360
|
}
|
|
395
361
|
|
|
396
362
|
export interface Lifecycles {
|
|
@@ -423,4 +389,4 @@ export interface StrapiDirectories {
|
|
|
423
389
|
middlewares: string;
|
|
424
390
|
config: string;
|
|
425
391
|
};
|
|
426
|
-
}
|
|
392
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@strapi/strapi",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.5.0-alpha.0",
|
|
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,18 +80,17 @@
|
|
|
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/
|
|
94
|
-
"@strapi/utils": "4.4.0-rc.1",
|
|
83
|
+
"@strapi/admin": "4.5.0-alpha.0",
|
|
84
|
+
"@strapi/database": "4.5.0-alpha.0",
|
|
85
|
+
"@strapi/generate-new": "4.5.0-alpha.0",
|
|
86
|
+
"@strapi/generators": "4.5.0-alpha.0",
|
|
87
|
+
"@strapi/logger": "4.5.0-alpha.0",
|
|
88
|
+
"@strapi/plugin-content-manager": "4.5.0-alpha.0",
|
|
89
|
+
"@strapi/plugin-content-type-builder": "4.5.0-alpha.0",
|
|
90
|
+
"@strapi/plugin-email": "4.5.0-alpha.0",
|
|
91
|
+
"@strapi/plugin-upload": "4.5.0-alpha.0",
|
|
92
|
+
"@strapi/typescript-utils": "4.5.0-alpha.0",
|
|
93
|
+
"@strapi/utils": "4.5.0-alpha.0",
|
|
95
94
|
"bcryptjs": "2.4.3",
|
|
96
95
|
"boxen": "5.1.2",
|
|
97
96
|
"chalk": "4.1.2",
|
|
@@ -140,5 +139,5 @@
|
|
|
140
139
|
"node": ">=14.19.1 <=18.x.x",
|
|
141
140
|
"npm": ">=6.0.0"
|
|
142
141
|
},
|
|
143
|
-
"gitHead": "
|
|
142
|
+
"gitHead": "c9a98c4dbcf3c4f2a449f8d96e7cbe4cd9b1e0f5"
|
|
144
143
|
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { has } = require('lodash/fp');
|
|
4
|
-
const validators = require('../../services/entity-validator/validators');
|
|
5
|
-
|
|
6
|
-
const customFieldsRegistry = (strapi) => {
|
|
7
|
-
const customFields = {};
|
|
8
|
-
|
|
9
|
-
return {
|
|
10
|
-
getAll() {
|
|
11
|
-
return customFields;
|
|
12
|
-
},
|
|
13
|
-
get(customField) {
|
|
14
|
-
const registeredCustomField = customFields[customField];
|
|
15
|
-
if (!registeredCustomField) {
|
|
16
|
-
throw new Error(`Could not find Custom Field: ${customField}`);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
return registeredCustomField;
|
|
20
|
-
},
|
|
21
|
-
add(customField) {
|
|
22
|
-
const customFieldList = Array.isArray(customField) ? customField : [customField];
|
|
23
|
-
|
|
24
|
-
for (const cf of customFieldList) {
|
|
25
|
-
if (!has('name', cf) || !has('type', cf)) {
|
|
26
|
-
throw new Error(`Custom fields require a 'name' and 'type' key`);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const { name, plugin, type } = cf;
|
|
30
|
-
if (!has(type, validators)) {
|
|
31
|
-
throw new Error(
|
|
32
|
-
`Custom field type: '${type}' is not a valid Strapi type or it can't be used with a Custom Field`
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const isValidObjectKey = /^(?![0-9])[a-zA-Z0-9$_-]+$/g;
|
|
37
|
-
if (!isValidObjectKey.test(name)) {
|
|
38
|
-
throw new Error(`Custom field name: '${name}' is not a valid object key`);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// When no plugin is specified, or it isn't found in Strapi, default to global
|
|
42
|
-
const uid = strapi.plugin(plugin) ? `plugin::${plugin}.${name}` : `global::${name}`;
|
|
43
|
-
|
|
44
|
-
if (has(uid, customFields)) {
|
|
45
|
-
throw new Error(`Custom field: '${uid}' has already been registered`);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
customFields[uid] = cf;
|
|
49
|
-
}
|
|
50
|
-
},
|
|
51
|
-
};
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
module.exports = customFieldsRegistry;
|
|
@@ -1,74 +0,0 @@
|
|
|
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;
|
|
@@ -1,148 +0,0 @@
|
|
|
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
|
-
};
|
|
@@ -1,19 +0,0 @@
|
|
|
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
|
-
};
|
|
@@ -1,19 +0,0 @@
|
|
|
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
|
-
};
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const convertCustomFieldType = (strapi) => {
|
|
4
|
-
const allContentTypeSchemaAttributes = Object.values(strapi.contentTypes).map(
|
|
5
|
-
(schema) => schema.attributes
|
|
6
|
-
);
|
|
7
|
-
const allComponentSchemaAttributes = Object.values(strapi.components).map(
|
|
8
|
-
(schema) => schema.attributes
|
|
9
|
-
);
|
|
10
|
-
const allSchemasAttributes = [...allContentTypeSchemaAttributes, ...allComponentSchemaAttributes];
|
|
11
|
-
|
|
12
|
-
for (const schemaAttrbutes of allSchemasAttributes) {
|
|
13
|
-
for (const attribute of Object.values(schemaAttrbutes)) {
|
|
14
|
-
if (attribute.type === 'customField') {
|
|
15
|
-
const customField = strapi.container.get('custom-fields').get(attribute.customField);
|
|
16
|
-
attribute.type = customField.type;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
module.exports = convertCustomFieldType;
|