@strapi/strapi 4.9.0-alpha.0 → 4.9.0-beta.2
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/bin/strapi.js +92 -60
- package/ee/index.js +6 -3
- package/ee/license.js +1 -1
- package/lib/Strapi.js +0 -4
- package/lib/commands/transfer/export.js +57 -28
- package/lib/commands/transfer/import.js +63 -23
- package/lib/commands/transfer/transfer.js +86 -33
- package/lib/commands/transfer/utils.js +82 -5
- package/lib/commands/utils/commander.js +17 -2
- package/lib/commands/utils/helpers.js +14 -7
- package/lib/core/registries/hooks.d.ts +1 -4
- package/lib/core/registries/policies.d.ts +5 -1
- package/lib/core-api/controller/collection-type.js +2 -3
- package/lib/core-api/controller/index.js +6 -0
- package/lib/core-api/controller/single-type.js +2 -2
- package/lib/global.d.ts +12 -7
- package/lib/index.d.ts +1 -1
- package/lib/services/auth/index.js +22 -1
- package/lib/services/cron.js +14 -1
- package/lib/services/entity-service/index.d.ts +1 -1
- package/lib/types/core/attributes/component.d.ts +4 -7
- package/lib/types/core/attributes/utils.d.ts +4 -5
- package/lib/types/core/index.d.ts +2 -2
- package/lib/types/index.d.ts +2 -2
- package/package.json +17 -17
- package/ee/ee-store.js +0 -18
|
@@ -1,56 +1,62 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { createTransferEngine } = require('@strapi/data-transfer/lib/engine');
|
|
4
3
|
const {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
engine: { createTransferEngine },
|
|
5
|
+
strapi: {
|
|
6
|
+
providers: {
|
|
7
|
+
createRemoteStrapiDestinationProvider,
|
|
8
|
+
createLocalStrapiSourceProvider,
|
|
9
|
+
createLocalStrapiDestinationProvider,
|
|
10
|
+
createRemoteStrapiSourceProvider,
|
|
11
|
+
},
|
|
9
12
|
},
|
|
10
|
-
} = require('@strapi/data-transfer
|
|
13
|
+
} = require('@strapi/data-transfer');
|
|
11
14
|
const { isObject } = require('lodash/fp');
|
|
12
|
-
const chalk = require('chalk');
|
|
13
15
|
|
|
14
16
|
const {
|
|
15
17
|
buildTransferTable,
|
|
16
18
|
createStrapiInstance,
|
|
17
19
|
DEFAULT_IGNORED_CONTENT_TYPES,
|
|
18
20
|
formatDiagnostic,
|
|
21
|
+
loadersFactory,
|
|
22
|
+
exitMessageText,
|
|
23
|
+
abortTransfer,
|
|
19
24
|
} = require('./utils');
|
|
20
|
-
|
|
21
|
-
const logger = console;
|
|
25
|
+
const { exitWith } = require('../utils/helpers');
|
|
22
26
|
|
|
23
27
|
/**
|
|
24
28
|
* @typedef TransferCommandOptions Options given to the CLI transfer command
|
|
25
29
|
*
|
|
26
30
|
* @property {URL|undefined} [to] The url of a remote Strapi to use as remote destination
|
|
27
31
|
* @property {URL|undefined} [from] The url of a remote Strapi to use as remote source
|
|
32
|
+
* @property {string|undefined} [toToken] The transfer token for the remote Strapi destination
|
|
33
|
+
* @property {string|undefined} [fromToken] The transfer token for the remote Strapi source
|
|
34
|
+
* @property {(keyof import('@strapi/data-transfer/src/engine').TransferGroupFilter)[]} [only] If present, only include these filtered groups of data
|
|
35
|
+
* @property {(keyof import('@strapi/data-transfer/src/engine').TransferGroupFilter)[]} [exclude] If present, exclude these filtered groups of data
|
|
36
|
+
* @property {number|undefined} [throttle] Delay in ms after each record
|
|
28
37
|
*/
|
|
29
38
|
|
|
30
39
|
/**
|
|
31
40
|
* Transfer command.
|
|
32
41
|
*
|
|
33
|
-
*
|
|
42
|
+
* Transfers data between local Strapi and remote Strapi instances
|
|
34
43
|
*
|
|
35
44
|
* @param {TransferCommandOptions} opts
|
|
36
45
|
*/
|
|
37
46
|
module.exports = async (opts) => {
|
|
38
47
|
// Validate inputs from Commander
|
|
39
48
|
if (!isObject(opts)) {
|
|
40
|
-
|
|
41
|
-
process.exit(1);
|
|
49
|
+
exitWith(1, 'Could not parse command arguments');
|
|
42
50
|
}
|
|
43
51
|
|
|
44
|
-
|
|
52
|
+
if (!(opts.from || opts.to) || (opts.from && opts.to)) {
|
|
53
|
+
exitWith(1, 'Exactly one source (from) or destination (to) option must be provided');
|
|
54
|
+
}
|
|
45
55
|
|
|
56
|
+
const strapi = await createStrapiInstance();
|
|
46
57
|
let source;
|
|
47
58
|
let destination;
|
|
48
59
|
|
|
49
|
-
if (!opts.from && !opts.to) {
|
|
50
|
-
logger.error('At least one source (from) or destination (to) option must be provided');
|
|
51
|
-
process.exit(1);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
60
|
// if no URL provided, use local Strapi
|
|
55
61
|
if (!opts.from) {
|
|
56
62
|
source = createLocalStrapiSourceProvider({
|
|
@@ -59,21 +65,42 @@ module.exports = async (opts) => {
|
|
|
59
65
|
}
|
|
60
66
|
// if URL provided, set up a remote source provider
|
|
61
67
|
else {
|
|
62
|
-
|
|
63
|
-
|
|
68
|
+
if (!opts.fromToken) {
|
|
69
|
+
exitWith(1, 'Missing token for remote destination');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
source = createRemoteStrapiSourceProvider({
|
|
73
|
+
getStrapi: () => strapi,
|
|
74
|
+
url: opts.from,
|
|
75
|
+
auth: {
|
|
76
|
+
type: 'token',
|
|
77
|
+
token: opts.fromToken,
|
|
78
|
+
},
|
|
79
|
+
});
|
|
64
80
|
}
|
|
65
81
|
|
|
66
82
|
// if no URL provided, use local Strapi
|
|
67
83
|
if (!opts.to) {
|
|
68
84
|
destination = createLocalStrapiDestinationProvider({
|
|
69
85
|
getStrapi: () => strapi,
|
|
86
|
+
strategy: 'restore',
|
|
87
|
+
restore: {
|
|
88
|
+
entities: { exclude: DEFAULT_IGNORED_CONTENT_TYPES },
|
|
89
|
+
},
|
|
70
90
|
});
|
|
71
91
|
}
|
|
72
92
|
// if URL provided, set up a remote destination provider
|
|
73
93
|
else {
|
|
94
|
+
if (!opts.toToken) {
|
|
95
|
+
exitWith(1, 'Missing token for remote destination');
|
|
96
|
+
}
|
|
97
|
+
|
|
74
98
|
destination = createRemoteStrapiDestinationProvider({
|
|
75
99
|
url: opts.to,
|
|
76
|
-
auth:
|
|
100
|
+
auth: {
|
|
101
|
+
type: 'token',
|
|
102
|
+
token: opts.toToken,
|
|
103
|
+
},
|
|
77
104
|
strategy: 'restore',
|
|
78
105
|
restore: {
|
|
79
106
|
entities: { exclude: DEFAULT_IGNORED_CONTENT_TYPES },
|
|
@@ -82,13 +109,15 @@ module.exports = async (opts) => {
|
|
|
82
109
|
}
|
|
83
110
|
|
|
84
111
|
if (!source || !destination) {
|
|
85
|
-
|
|
86
|
-
process.exit(1);
|
|
112
|
+
exitWith(1, 'Could not create providers');
|
|
87
113
|
}
|
|
88
114
|
|
|
89
115
|
const engine = createTransferEngine(source, destination, {
|
|
90
|
-
versionStrategy: '
|
|
116
|
+
versionStrategy: 'exact',
|
|
91
117
|
schemaStrategy: 'strict',
|
|
118
|
+
exclude: opts.exclude,
|
|
119
|
+
only: opts.only,
|
|
120
|
+
throttle: opts.throttle,
|
|
92
121
|
transforms: {
|
|
93
122
|
links: [
|
|
94
123
|
{
|
|
@@ -112,18 +141,42 @@ module.exports = async (opts) => {
|
|
|
112
141
|
|
|
113
142
|
engine.diagnostics.onDiagnostic(formatDiagnostic('transfer'));
|
|
114
143
|
|
|
115
|
-
|
|
116
|
-
|
|
144
|
+
const progress = engine.progress.stream;
|
|
145
|
+
|
|
146
|
+
const { updateLoader } = loadersFactory();
|
|
117
147
|
|
|
118
|
-
|
|
148
|
+
progress.on(`stage::start`, ({ stage, data }) => {
|
|
149
|
+
updateLoader(stage, data).start();
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
progress.on('stage::finish', ({ stage, data }) => {
|
|
153
|
+
updateLoader(stage, data).succeed();
|
|
154
|
+
});
|
|
119
155
|
|
|
120
|
-
|
|
121
|
-
|
|
156
|
+
progress.on('stage::progress', ({ stage, data }) => {
|
|
157
|
+
updateLoader(stage, data);
|
|
158
|
+
});
|
|
122
159
|
|
|
123
|
-
|
|
124
|
-
|
|
160
|
+
progress.on('stage::error', ({ stage, data }) => {
|
|
161
|
+
updateLoader(stage, data).fail();
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
let results;
|
|
165
|
+
try {
|
|
166
|
+
console.log(`Starting transfer...`);
|
|
167
|
+
|
|
168
|
+
// Abort transfer if user interrupts process
|
|
169
|
+
['SIGTERM', 'SIGINT', 'SIGQUIT'].forEach((signal) => {
|
|
170
|
+
process.removeAllListeners(signal);
|
|
171
|
+
process.on(signal, () => abortTransfer({ engine, strapi }));
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
results = await engine.transfer();
|
|
125
175
|
} catch (e) {
|
|
126
|
-
|
|
127
|
-
process.exit(1);
|
|
176
|
+
exitWith(1, exitMessageText('transfer', true));
|
|
128
177
|
}
|
|
178
|
+
|
|
179
|
+
const table = buildTransferTable(results.engine);
|
|
180
|
+
console.log(table.toString());
|
|
181
|
+
exitWith(0, exitMessageText('transfer'));
|
|
129
182
|
};
|
|
@@ -3,15 +3,30 @@
|
|
|
3
3
|
const chalk = require('chalk');
|
|
4
4
|
const Table = require('cli-table3');
|
|
5
5
|
const { Option } = require('commander');
|
|
6
|
-
const {
|
|
6
|
+
const {
|
|
7
|
+
engine: { TransferGroupPresets },
|
|
8
|
+
} = require('@strapi/data-transfer');
|
|
7
9
|
|
|
8
10
|
const {
|
|
9
11
|
configs: { createOutputFileConfiguration },
|
|
10
12
|
createLogger,
|
|
11
13
|
} = require('@strapi/logger');
|
|
14
|
+
const ora = require('ora');
|
|
12
15
|
const { readableBytes, exitWith } = require('../utils/helpers');
|
|
13
16
|
const strapi = require('../../index');
|
|
14
|
-
const { getParseListWithChoices } = require('../utils/commander');
|
|
17
|
+
const { getParseListWithChoices, parseInteger } = require('../utils/commander');
|
|
18
|
+
|
|
19
|
+
const exitMessageText = (process, error = false) => {
|
|
20
|
+
const processCapitalized = process[0].toUpperCase() + process.slice(1);
|
|
21
|
+
|
|
22
|
+
if (!error) {
|
|
23
|
+
return chalk.bold(
|
|
24
|
+
chalk.green(`${processCapitalized} process has been completed successfully!`)
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return chalk.bold(chalk.red(`${processCapitalized} process failed.`));
|
|
29
|
+
};
|
|
15
30
|
|
|
16
31
|
const pad = (n) => {
|
|
17
32
|
return (n < 10 ? '0' : '') + String(n);
|
|
@@ -82,15 +97,28 @@ const DEFAULT_IGNORED_CONTENT_TYPES = [
|
|
|
82
97
|
'admin::role',
|
|
83
98
|
'admin::api-token',
|
|
84
99
|
'admin::api-token-permission',
|
|
100
|
+
'admin::transfer-token',
|
|
101
|
+
'admin::transfer-token-permission',
|
|
85
102
|
'admin::audit-log',
|
|
86
103
|
];
|
|
87
104
|
|
|
88
|
-
const
|
|
105
|
+
const abortTransfer = async ({ engine, strapi }) => {
|
|
106
|
+
try {
|
|
107
|
+
await engine.abortTransfer();
|
|
108
|
+
await strapi.destroy();
|
|
109
|
+
} catch (e) {
|
|
110
|
+
// ignore because there's not much else we can do
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
return true;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const createStrapiInstance = async (opts = {}) => {
|
|
89
117
|
try {
|
|
90
118
|
const appContext = await strapi.compile();
|
|
91
|
-
const app = strapi(appContext);
|
|
119
|
+
const app = strapi({ ...opts, ...appContext });
|
|
92
120
|
|
|
93
|
-
app.log.level = logLevel;
|
|
121
|
+
app.log.level = opts.logLevel || 'error';
|
|
94
122
|
return await app.load();
|
|
95
123
|
} catch (err) {
|
|
96
124
|
if (err.code === 'ECONNREFUSED') {
|
|
@@ -102,6 +130,13 @@ const createStrapiInstance = async (logLevel = 'error') => {
|
|
|
102
130
|
|
|
103
131
|
const transferDataTypes = Object.keys(TransferGroupPresets);
|
|
104
132
|
|
|
133
|
+
const throttleOption = new Option(
|
|
134
|
+
'--throttle <delay after each entity>',
|
|
135
|
+
`Add a delay in milliseconds between each transferred entity`
|
|
136
|
+
)
|
|
137
|
+
.argParser(parseInteger)
|
|
138
|
+
.hideHelp(); // This option is not publicly documented
|
|
139
|
+
|
|
105
140
|
const excludeOption = new Option(
|
|
106
141
|
'--exclude <comma-separated data types>',
|
|
107
142
|
`Exclude data using comma-separated types. Available types: ${transferDataTypes.join(',')}`
|
|
@@ -169,13 +204,55 @@ const formatDiagnostic =
|
|
|
169
204
|
}
|
|
170
205
|
};
|
|
171
206
|
|
|
207
|
+
const loadersFactory = (defaultLoaders = {}) => {
|
|
208
|
+
const loaders = defaultLoaders;
|
|
209
|
+
const updateLoader = (stage, data) => {
|
|
210
|
+
if (!(stage in loaders)) {
|
|
211
|
+
createLoader(stage);
|
|
212
|
+
}
|
|
213
|
+
const stageData = data[stage];
|
|
214
|
+
const elapsedTime = stageData?.startTime
|
|
215
|
+
? (stageData?.endTime || Date.now()) - stageData.startTime
|
|
216
|
+
: 0;
|
|
217
|
+
const size = `size: ${readableBytes(stageData?.bytes ?? 0)}`;
|
|
218
|
+
const elapsed = `elapsed: ${elapsedTime} ms`;
|
|
219
|
+
const speed =
|
|
220
|
+
elapsedTime > 0 ? `(${readableBytes(((stageData?.bytes ?? 0) * 1000) / elapsedTime)}/s)` : '';
|
|
221
|
+
|
|
222
|
+
loaders[stage].text = `${stage}: ${stageData?.count ?? 0} transfered (${size}) (${elapsed}) ${
|
|
223
|
+
!stageData?.endTime ? speed : ''
|
|
224
|
+
}`;
|
|
225
|
+
|
|
226
|
+
return loaders[stage];
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
const createLoader = (stage) => {
|
|
230
|
+
Object.assign(loaders, { [stage]: ora() });
|
|
231
|
+
return loaders[stage];
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
const getLoader = (stage) => {
|
|
235
|
+
return loaders[stage];
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
updateLoader,
|
|
240
|
+
createLoader,
|
|
241
|
+
getLoader,
|
|
242
|
+
};
|
|
243
|
+
};
|
|
244
|
+
|
|
172
245
|
module.exports = {
|
|
246
|
+
loadersFactory,
|
|
173
247
|
buildTransferTable,
|
|
174
248
|
getDefaultExportName,
|
|
175
249
|
DEFAULT_IGNORED_CONTENT_TYPES,
|
|
176
250
|
createStrapiInstance,
|
|
177
251
|
excludeOption,
|
|
252
|
+
exitMessageText,
|
|
178
253
|
onlyOption,
|
|
254
|
+
throttleOption,
|
|
179
255
|
validateExcludeOnly,
|
|
180
256
|
formatDiagnostic,
|
|
257
|
+
abortTransfer,
|
|
181
258
|
};
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
const inquirer = require('inquirer');
|
|
8
8
|
const { InvalidOptionArgumentError, Option } = require('commander');
|
|
9
9
|
const { bold, green, cyan } = require('chalk');
|
|
10
|
+
const { isNaN } = require('lodash/fp');
|
|
10
11
|
const { exitWith } = require('./helpers');
|
|
11
12
|
|
|
12
13
|
/**
|
|
@@ -40,6 +41,18 @@ const getParseListWithChoices = (choices, errorMessage = 'Invalid options:') =>
|
|
|
40
41
|
};
|
|
41
42
|
};
|
|
42
43
|
|
|
44
|
+
/**
|
|
45
|
+
* argParser: Parse a string as an integer
|
|
46
|
+
*/
|
|
47
|
+
const parseInteger = (value) => {
|
|
48
|
+
// parseInt takes a string and a radix
|
|
49
|
+
const parsedValue = parseInt(value, 10);
|
|
50
|
+
if (isNaN(parsedValue)) {
|
|
51
|
+
throw new InvalidOptionArgumentError(`Not an integer: ${value}`);
|
|
52
|
+
}
|
|
53
|
+
return parsedValue;
|
|
54
|
+
};
|
|
55
|
+
|
|
43
56
|
/**
|
|
44
57
|
* argParser: Parse a string as a URL object
|
|
45
58
|
*/
|
|
@@ -96,8 +109,9 @@ const promptEncryptionKey = async (thisCommand) => {
|
|
|
96
109
|
*
|
|
97
110
|
* @param {string} message The message to confirm with user
|
|
98
111
|
* @param {object} options Additional options
|
|
112
|
+
* @param {string|undefined} options.failMessage The message to display when prompt is not confirmed
|
|
99
113
|
*/
|
|
100
|
-
const confirmMessage = (message) => {
|
|
114
|
+
const confirmMessage = (message, { failMessage } = {}) => {
|
|
101
115
|
return async (command) => {
|
|
102
116
|
// if we have a force option, assume yes
|
|
103
117
|
const opts = command.opts();
|
|
@@ -116,7 +130,7 @@ const confirmMessage = (message) => {
|
|
|
116
130
|
},
|
|
117
131
|
]);
|
|
118
132
|
if (!answers.confirm) {
|
|
119
|
-
exitWith(
|
|
133
|
+
exitWith(1, failMessage);
|
|
120
134
|
}
|
|
121
135
|
};
|
|
122
136
|
};
|
|
@@ -130,6 +144,7 @@ module.exports = {
|
|
|
130
144
|
getParseListWithChoices,
|
|
131
145
|
parseList,
|
|
132
146
|
parseURL,
|
|
147
|
+
parseInteger,
|
|
133
148
|
promptEncryptionKey,
|
|
134
149
|
confirmMessage,
|
|
135
150
|
forceOption,
|
|
@@ -36,22 +36,29 @@ const readableBytes = (bytes, decimals = 1, padStart = 0) => {
|
|
|
36
36
|
*
|
|
37
37
|
* @param {number} code Code to exit process with
|
|
38
38
|
* @param {string | Array} message Message(s) to display before exiting
|
|
39
|
+
* @param {Object} options
|
|
40
|
+
* @param {console} options.logger - logger object, defaults to console
|
|
41
|
+
* @param {process} options.prc - process object, defaults to process
|
|
42
|
+
*
|
|
39
43
|
*/
|
|
40
|
-
const exitWith = (code, message = undefined) => {
|
|
41
|
-
const logger =
|
|
44
|
+
const exitWith = (code, message = undefined, options = {}) => {
|
|
45
|
+
const { logger = console, prc = process } = options;
|
|
46
|
+
|
|
47
|
+
const log = (message) => {
|
|
42
48
|
if (code === 0) {
|
|
43
|
-
|
|
49
|
+
logger.log(chalk.green(message));
|
|
44
50
|
} else {
|
|
45
|
-
|
|
51
|
+
logger.error(chalk.red(message));
|
|
46
52
|
}
|
|
47
53
|
};
|
|
48
54
|
|
|
49
55
|
if (isString(message)) {
|
|
50
|
-
|
|
56
|
+
log(message);
|
|
51
57
|
} else if (isArray(message)) {
|
|
52
|
-
message.forEach((msg) =>
|
|
58
|
+
message.forEach((msg) => log(msg));
|
|
53
59
|
}
|
|
54
|
-
|
|
60
|
+
|
|
61
|
+
prc.exit(code);
|
|
55
62
|
};
|
|
56
63
|
|
|
57
64
|
/**
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
type Handler = (context: any) => any;
|
|
3
2
|
|
|
4
3
|
type AsyncHook = {
|
|
@@ -8,7 +7,6 @@ type AsyncHook = {
|
|
|
8
7
|
call(): Promise<void>;
|
|
9
8
|
};
|
|
10
9
|
|
|
11
|
-
|
|
12
10
|
type SyncHook = {
|
|
13
11
|
get handlers(): Handler[];
|
|
14
12
|
register(handler: Handler): this;
|
|
@@ -16,5 +14,4 @@ type SyncHook = {
|
|
|
16
14
|
call(): void;
|
|
17
15
|
};
|
|
18
16
|
|
|
19
|
-
|
|
20
|
-
export type Hook = AsyncHook|SyncHook
|
|
17
|
+
export type Hook = AsyncHook | SyncHook;
|
|
@@ -6,4 +6,8 @@ interface PolicyContext extends BaseContext {
|
|
|
6
6
|
is(name): boolean;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
export type Policy<T=unknown> = (
|
|
9
|
+
export type Policy<T = unknown> = (
|
|
10
|
+
ctx: PolicyContext,
|
|
11
|
+
cfg: T,
|
|
12
|
+
{ strapi: Strapi }
|
|
13
|
+
) => boolean | undefined;
|
|
@@ -19,9 +19,8 @@ const createCollectionTypeController = ({ contentType }) => {
|
|
|
19
19
|
* @return {Object|Array}
|
|
20
20
|
*/
|
|
21
21
|
async find(ctx) {
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
const { results, pagination } = await strapi.service(uid).find(query);
|
|
22
|
+
const sanitizedQuery = await this.sanitizeQuery(ctx);
|
|
23
|
+
const { results, pagination } = await strapi.service(uid).find(sanitizedQuery);
|
|
25
24
|
const sanitizedResults = await this.sanitizeOutput(results, ctx);
|
|
26
25
|
|
|
27
26
|
return this.transformResponse(sanitizedResults, { pagination });
|
|
@@ -29,6 +29,12 @@ const createController = ({ contentType }) => {
|
|
|
29
29
|
|
|
30
30
|
return sanitize.contentAPI.input(data, contentType, { auth });
|
|
31
31
|
},
|
|
32
|
+
|
|
33
|
+
sanitizeQuery(ctx) {
|
|
34
|
+
const auth = getAuthFromKoaContext(ctx);
|
|
35
|
+
|
|
36
|
+
return sanitize.contentAPI.query(ctx.query, contentType, { auth });
|
|
37
|
+
},
|
|
32
38
|
};
|
|
33
39
|
|
|
34
40
|
let ctrl;
|
|
@@ -18,9 +18,9 @@ const createSingleTypeController = ({ contentType }) => {
|
|
|
18
18
|
* @return {Object|Array}
|
|
19
19
|
*/
|
|
20
20
|
async find(ctx) {
|
|
21
|
-
const
|
|
21
|
+
const sanitizedQuery = await this.sanitizeQuery(ctx);
|
|
22
|
+
const entity = await strapi.service(uid).find(sanitizedQuery);
|
|
22
23
|
|
|
23
|
-
const entity = await strapi.service(uid).find(query);
|
|
24
24
|
const sanitizedEntity = await this.sanitizeOutput(entity, ctx);
|
|
25
25
|
|
|
26
26
|
return this.transformResponse(sanitizedEntity);
|
package/lib/global.d.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import type { Strapi as StrapiInterface } from './types/core';
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
CollectionTypeSchema,
|
|
4
|
+
SingleTypeSchema,
|
|
5
|
+
ComponentSchema,
|
|
6
|
+
ContentTypeSchema,
|
|
7
|
+
} from './types/core/schemas';
|
|
3
8
|
import type { KeysBy } from './types/utils';
|
|
4
9
|
|
|
5
10
|
declare global {
|
|
@@ -7,7 +12,7 @@ declare global {
|
|
|
7
12
|
/**
|
|
8
13
|
* Map of UID / schemas used as a schemas database for other types.
|
|
9
14
|
* It must be extended by the user application or plugins.
|
|
10
|
-
*
|
|
15
|
+
*
|
|
11
16
|
* @example
|
|
12
17
|
* ```ts
|
|
13
18
|
* declare global {
|
|
@@ -39,12 +44,12 @@ declare global {
|
|
|
39
44
|
/**
|
|
40
45
|
* Literal union type of every component registered in Strapi.Schemas
|
|
41
46
|
*/
|
|
42
|
-
|
|
47
|
+
type ComponentUIDs = KeysBy<Schemas, ComponentSchema>;
|
|
43
48
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
49
|
+
/**
|
|
50
|
+
* Global shorthand to access the `StrapiInterface` type
|
|
51
|
+
*/
|
|
52
|
+
type Strapi = StrapiInterface;
|
|
48
53
|
}
|
|
49
54
|
|
|
50
55
|
/**
|
package/lib/index.d.ts
CHANGED
|
@@ -43,7 +43,28 @@ const createAuthentication = () => {
|
|
|
43
43
|
return next();
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
const
|
|
46
|
+
const routeStrategies = strategies[route.info.type];
|
|
47
|
+
const configStrategies = config?.strategies ?? routeStrategies ?? [];
|
|
48
|
+
|
|
49
|
+
const strategiesToUse = configStrategies.reduce((acc, strategy) => {
|
|
50
|
+
// Resolve by strategy name
|
|
51
|
+
if (typeof strategy === 'string') {
|
|
52
|
+
const routeStrategy = routeStrategies.find((rs) => rs.name === strategy);
|
|
53
|
+
|
|
54
|
+
if (routeStrategy) {
|
|
55
|
+
acc.push(routeStrategy);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Use the given strategy as is
|
|
60
|
+
else if (typeof strategy === 'object') {
|
|
61
|
+
validStrategy(strategy);
|
|
62
|
+
|
|
63
|
+
acc.push(strategy);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return acc;
|
|
67
|
+
}, []);
|
|
47
68
|
|
|
48
69
|
for (const strategy of strategiesToUse) {
|
|
49
70
|
const result = await strategy.authenticate(ctx);
|
package/lib/services/cron.js
CHANGED
|
@@ -14,10 +14,15 @@ const createCronService = () => {
|
|
|
14
14
|
|
|
15
15
|
let fn;
|
|
16
16
|
let options;
|
|
17
|
+
let taskName;
|
|
17
18
|
if (isFunction(taskValue)) {
|
|
19
|
+
// don't use task name if key is the rule
|
|
20
|
+
taskName = null;
|
|
18
21
|
fn = taskValue.bind(tasks);
|
|
19
22
|
options = taskExpression;
|
|
20
23
|
} else if (isFunction(taskValue.task)) {
|
|
24
|
+
// set task name if key is not the rule
|
|
25
|
+
taskName = taskExpression;
|
|
21
26
|
fn = taskValue.task.bind(taskValue);
|
|
22
27
|
options = taskValue.options;
|
|
23
28
|
} else {
|
|
@@ -29,7 +34,7 @@ const createCronService = () => {
|
|
|
29
34
|
const fnWithStrapi = (...args) => fn({ strapi }, ...args);
|
|
30
35
|
|
|
31
36
|
const job = new Job(null, fnWithStrapi);
|
|
32
|
-
jobsSpecs.push({ job, options });
|
|
37
|
+
jobsSpecs.push({ job, options, name: taskName });
|
|
33
38
|
|
|
34
39
|
if (running) {
|
|
35
40
|
job.schedule(options);
|
|
@@ -37,6 +42,13 @@ const createCronService = () => {
|
|
|
37
42
|
}
|
|
38
43
|
return this;
|
|
39
44
|
},
|
|
45
|
+
remove(name) {
|
|
46
|
+
if (!name) throw new Error('You must provide a name to remove a cron job.');
|
|
47
|
+
const matchingJobsSpecs = jobsSpecs.filter(({ name: jobSpecName }) => jobSpecName === name);
|
|
48
|
+
matchingJobsSpecs.forEach(({ job }) => job.cancel());
|
|
49
|
+
jobsSpecs = jobsSpecs.filter(({ name: jobSpecName }) => jobSpecName !== name);
|
|
50
|
+
return this;
|
|
51
|
+
},
|
|
40
52
|
start() {
|
|
41
53
|
jobsSpecs.forEach(({ job, options }) => job.schedule(options));
|
|
42
54
|
running = true;
|
|
@@ -52,6 +64,7 @@ const createCronService = () => {
|
|
|
52
64
|
jobsSpecs = [];
|
|
53
65
|
return this;
|
|
54
66
|
},
|
|
67
|
+
jobs: jobsSpecs,
|
|
55
68
|
};
|
|
56
69
|
};
|
|
57
70
|
|
|
@@ -25,13 +25,10 @@ export type ComponentAttribute<
|
|
|
25
25
|
PrivateOption &
|
|
26
26
|
RequiredOption;
|
|
27
27
|
|
|
28
|
-
export type ComponentValue<
|
|
29
|
-
T
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
? V[]
|
|
33
|
-
: V
|
|
34
|
-
: never;
|
|
28
|
+
export type ComponentValue<
|
|
29
|
+
T extends Strapi.ComponentUIDs,
|
|
30
|
+
R extends boolean
|
|
31
|
+
> = GetAttributesValues<T> extends infer V ? (R extends true ? V[] : V) : never;
|
|
35
32
|
|
|
36
33
|
export type GetComponentAttributeValue<T extends Attribute> = T extends ComponentAttribute<
|
|
37
34
|
infer U,
|
|
@@ -83,11 +83,10 @@ export type GetAttributeValueByKey<
|
|
|
83
83
|
export type GetAttributesValues<T extends SchemaUID> = {
|
|
84
84
|
// Handle required attributes
|
|
85
85
|
[key in GetAttributesRequiredKeys<T>]-?: GetAttributeValueByKey<T, key>;
|
|
86
|
-
} &
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
};
|
|
86
|
+
} & {
|
|
87
|
+
// Handle optional attributes
|
|
88
|
+
[key in GetAttributesOptionalKeys<T>]?: GetAttributeValueByKey<T, key>;
|
|
89
|
+
};
|
|
91
90
|
|
|
92
91
|
export type GetAttributesRequiredKeys<T extends SchemaUID> = KeysBy<
|
|
93
92
|
GetAttributes<T>,
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export * from './attributes';
|
|
2
|
-
export * from
|
|
3
|
-
export * from './strapi'
|
|
2
|
+
export * from './schemas';
|
|
3
|
+
export * from './strapi';
|
package/lib/types/index.d.ts
CHANGED