@strapi/strapi 4.10.4 → 4.10.5
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/commands/actions/export/action.js +2 -4
- package/lib/commands/actions/import/action.js +5 -4
- package/lib/commands/actions/import/command.js +2 -2
- package/lib/commands/actions/transfer/action.js +5 -4
- package/lib/commands/actions/transfer/command.js +3 -3
- package/lib/commands/utils/commander.js +24 -18
- package/lib/commands/utils/data-transfer.js +87 -1
- package/lib/types/core/strapi/index.d.ts +8 -0
- package/package.json +16 -16
|
@@ -25,6 +25,7 @@ const {
|
|
|
25
25
|
exitMessageText,
|
|
26
26
|
abortTransfer,
|
|
27
27
|
getTransferTelemetryPayload,
|
|
28
|
+
setSignalHandler,
|
|
28
29
|
} = require('../../utils/data-transfer');
|
|
29
30
|
const { exitWith } = require('../../utils/helpers');
|
|
30
31
|
|
|
@@ -115,10 +116,7 @@ module.exports = async (opts) => {
|
|
|
115
116
|
let outFile;
|
|
116
117
|
try {
|
|
117
118
|
// Abort transfer if user interrupts process
|
|
118
|
-
|
|
119
|
-
process.removeAllListeners(signal);
|
|
120
|
-
process.on(signal, () => abortTransfer({ engine, strapi }));
|
|
121
|
-
});
|
|
119
|
+
setSignalHandler(() => abortTransfer({ engine, strapi }));
|
|
122
120
|
|
|
123
121
|
results = await engine.transfer();
|
|
124
122
|
outFile = results.destination.file.path;
|
|
@@ -21,6 +21,8 @@ const {
|
|
|
21
21
|
exitMessageText,
|
|
22
22
|
abortTransfer,
|
|
23
23
|
getTransferTelemetryPayload,
|
|
24
|
+
setSignalHandler,
|
|
25
|
+
getDiffHandler,
|
|
24
26
|
} = require('../../utils/data-transfer');
|
|
25
27
|
const { exitWith } = require('../../utils/helpers');
|
|
26
28
|
|
|
@@ -111,6 +113,8 @@ module.exports = async (opts) => {
|
|
|
111
113
|
|
|
112
114
|
const { updateLoader } = loadersFactory();
|
|
113
115
|
|
|
116
|
+
engine.onSchemaDiff(getDiffHandler(engine, { force: opts.force, action: 'import' }));
|
|
117
|
+
|
|
114
118
|
progress.on(`stage::start`, ({ stage, data }) => {
|
|
115
119
|
updateLoader(stage, data).start();
|
|
116
120
|
});
|
|
@@ -134,10 +138,7 @@ module.exports = async (opts) => {
|
|
|
134
138
|
let results;
|
|
135
139
|
try {
|
|
136
140
|
// Abort transfer if user interrupts process
|
|
137
|
-
|
|
138
|
-
process.removeAllListeners(signal);
|
|
139
|
-
process.on(signal, () => abortTransfer({ engine, strapi }));
|
|
140
|
-
});
|
|
141
|
+
setSignalHandler(() => abortTransfer({ engine, strapi }));
|
|
141
142
|
|
|
142
143
|
results = await engine.transfer();
|
|
143
144
|
} catch (e) {
|
|
@@ -9,7 +9,7 @@ const {
|
|
|
9
9
|
throttleOption,
|
|
10
10
|
validateExcludeOnly,
|
|
11
11
|
} = require('../../utils/data-transfer');
|
|
12
|
-
const {
|
|
12
|
+
const { getCommanderConfirmMessage, forceOption } = require('../../utils/commander');
|
|
13
13
|
const { getLocalScript, exitWith } = require('../../utils/helpers');
|
|
14
14
|
|
|
15
15
|
/**
|
|
@@ -88,7 +88,7 @@ module.exports = ({ command }) => {
|
|
|
88
88
|
})
|
|
89
89
|
.hook(
|
|
90
90
|
'preAction',
|
|
91
|
-
|
|
91
|
+
getCommanderConfirmMessage(
|
|
92
92
|
'The import will delete all assets and data in your database. Are you sure you want to proceed?',
|
|
93
93
|
{ failMessage: 'Import process aborted' }
|
|
94
94
|
)
|
|
@@ -22,6 +22,8 @@ const {
|
|
|
22
22
|
exitMessageText,
|
|
23
23
|
abortTransfer,
|
|
24
24
|
getTransferTelemetryPayload,
|
|
25
|
+
setSignalHandler,
|
|
26
|
+
getDiffHandler,
|
|
25
27
|
} = require('../../utils/data-transfer');
|
|
26
28
|
const { exitWith } = require('../../utils/helpers');
|
|
27
29
|
|
|
@@ -146,6 +148,8 @@ module.exports = async (opts) => {
|
|
|
146
148
|
|
|
147
149
|
const { updateLoader } = loadersFactory();
|
|
148
150
|
|
|
151
|
+
engine.onSchemaDiff(getDiffHandler(engine, { force: opts.force, action: 'transfer' }));
|
|
152
|
+
|
|
149
153
|
progress.on(`stage::start`, ({ stage, data }) => {
|
|
150
154
|
updateLoader(stage, data).start();
|
|
151
155
|
});
|
|
@@ -171,10 +175,7 @@ module.exports = async (opts) => {
|
|
|
171
175
|
let results;
|
|
172
176
|
try {
|
|
173
177
|
// Abort transfer if user interrupts process
|
|
174
|
-
|
|
175
|
-
process.removeAllListeners(signal);
|
|
176
|
-
process.on(signal, () => abortTransfer({ engine, strapi }));
|
|
177
|
-
});
|
|
178
|
+
setSignalHandler(() => abortTransfer({ engine, strapi }));
|
|
178
179
|
|
|
179
180
|
results = await engine.transfer();
|
|
180
181
|
} catch (e) {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const inquirer = require('inquirer');
|
|
4
4
|
const { Option } = require('commander');
|
|
5
|
-
const {
|
|
5
|
+
const { getCommanderConfirmMessage, forceOption, parseURL } = require('../../utils/commander');
|
|
6
6
|
const {
|
|
7
7
|
getLocalScript,
|
|
8
8
|
exitWith,
|
|
@@ -76,7 +76,7 @@ module.exports = ({ command }) => {
|
|
|
76
76
|
thisCommand.opts().fromToken = answers.fromToken;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
await
|
|
79
|
+
await getCommanderConfirmMessage(
|
|
80
80
|
'The transfer will delete all the local Strapi assets and its database. Are you sure you want to proceed?',
|
|
81
81
|
{ failMessage: 'Transfer process aborted' }
|
|
82
82
|
)(thisCommand);
|
|
@@ -104,7 +104,7 @@ module.exports = ({ command }) => {
|
|
|
104
104
|
thisCommand.opts().toToken = answers.toToken;
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
await
|
|
107
|
+
await getCommanderConfirmMessage(
|
|
108
108
|
'The transfer will delete all the remote Strapi assets and its database. Are you sure you want to proceed?',
|
|
109
109
|
{ failMessage: 'Transfer process aborted' }
|
|
110
110
|
)(thisCommand);
|
|
@@ -111,30 +111,35 @@ const promptEncryptionKey = async (thisCommand) => {
|
|
|
111
111
|
* @param {object} options Additional options
|
|
112
112
|
* @param {string|undefined} options.failMessage The message to display when prompt is not confirmed
|
|
113
113
|
*/
|
|
114
|
-
const
|
|
114
|
+
const getCommanderConfirmMessage = (message, { failMessage } = {}) => {
|
|
115
115
|
return async (command) => {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
if (opts?.force === true) {
|
|
119
|
-
// attempt to mimic the inquirer prompt exactly
|
|
120
|
-
console.log(`${green('?')} ${bold(message)} ${cyan('Yes')}`);
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const answers = await inquirer.prompt([
|
|
125
|
-
{
|
|
126
|
-
type: 'confirm',
|
|
127
|
-
message,
|
|
128
|
-
name: `confirm`,
|
|
129
|
-
default: false,
|
|
130
|
-
},
|
|
131
|
-
]);
|
|
132
|
-
if (!answers.confirm) {
|
|
116
|
+
const confirmed = await confirmMessage(message, { force: command.opts().force });
|
|
117
|
+
if (!confirmed) {
|
|
133
118
|
exitWith(1, failMessage);
|
|
134
119
|
}
|
|
135
120
|
};
|
|
136
121
|
};
|
|
137
122
|
|
|
123
|
+
const confirmMessage = async (message, { force } = {}) => {
|
|
124
|
+
// if we have a force option, respond yes
|
|
125
|
+
if (force === true) {
|
|
126
|
+
// attempt to mimic the inquirer prompt exactly
|
|
127
|
+
console.log(`${green('?')} ${bold(message)} ${cyan('Yes')}`);
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const answers = await inquirer.prompt([
|
|
132
|
+
{
|
|
133
|
+
type: 'confirm',
|
|
134
|
+
message,
|
|
135
|
+
name: `confirm`,
|
|
136
|
+
default: false,
|
|
137
|
+
},
|
|
138
|
+
]);
|
|
139
|
+
|
|
140
|
+
return answers.confirm;
|
|
141
|
+
};
|
|
142
|
+
|
|
138
143
|
const forceOption = new Option(
|
|
139
144
|
'--force',
|
|
140
145
|
`Automatically answer "yes" to all prompts, including potentially destructive requests, and run non-interactively.`
|
|
@@ -146,6 +151,7 @@ module.exports = {
|
|
|
146
151
|
parseURL,
|
|
147
152
|
parseInteger,
|
|
148
153
|
promptEncryptionKey,
|
|
154
|
+
getCommanderConfirmMessage,
|
|
149
155
|
confirmMessage,
|
|
150
156
|
forceOption,
|
|
151
157
|
};
|
|
@@ -12,9 +12,11 @@ const {
|
|
|
12
12
|
createLogger,
|
|
13
13
|
} = require('@strapi/logger');
|
|
14
14
|
const ora = require('ora');
|
|
15
|
+
const { TransferEngineInitializationError } = require('@strapi/data-transfer/dist/engine/errors');
|
|
16
|
+
const { merge } = require('lodash/fp');
|
|
15
17
|
const { readableBytes, exitWith } = require('./helpers');
|
|
16
18
|
const strapi = require('../../index');
|
|
17
|
-
const { getParseListWithChoices, parseInteger } = require('./commander');
|
|
19
|
+
const { getParseListWithChoices, parseInteger, confirmMessage } = require('./commander');
|
|
18
20
|
|
|
19
21
|
const exitMessageText = (process, error = false) => {
|
|
20
22
|
const processCapitalized = process[0].toUpperCase() + process.slice(1);
|
|
@@ -113,6 +115,15 @@ const abortTransfer = async ({ engine, strapi }) => {
|
|
|
113
115
|
return true;
|
|
114
116
|
};
|
|
115
117
|
|
|
118
|
+
const setSignalHandler = async (handler, signals = ['SIGINT', 'SIGTERM', 'SIGQUIT']) => {
|
|
119
|
+
signals.forEach((signal) => {
|
|
120
|
+
// We specifically remove ALL listeners because we have to clear the one added in Strapi bootstrap that has a process.exit
|
|
121
|
+
// TODO: Ideally Strapi bootstrap would not add that listener, and then this could be more flexible and add/remove only what it needs to
|
|
122
|
+
process.removeAllListeners(signal);
|
|
123
|
+
process.on(signal, handler);
|
|
124
|
+
});
|
|
125
|
+
};
|
|
126
|
+
|
|
116
127
|
const createStrapiInstance = async (opts = {}) => {
|
|
117
128
|
try {
|
|
118
129
|
const appContext = await strapi.compile();
|
|
@@ -257,6 +268,79 @@ const getTransferTelemetryPayload = (engine) => {
|
|
|
257
268
|
};
|
|
258
269
|
};
|
|
259
270
|
|
|
271
|
+
/**
|
|
272
|
+
* Get a transfer engine schema diff handler that confirms with the user before bypassing a schema check
|
|
273
|
+
*/
|
|
274
|
+
const getDiffHandler = (engine, { force, action }) => {
|
|
275
|
+
return async (context, next) => {
|
|
276
|
+
// if we abort here, we need to actually exit the process because of conflict with inquirer prompt
|
|
277
|
+
setSignalHandler(async () => {
|
|
278
|
+
await abortTransfer({ engine, strapi });
|
|
279
|
+
exitWith(1, exitMessageText(action, true));
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
let workflowsStatus;
|
|
283
|
+
const source = 'Schema Integrity';
|
|
284
|
+
|
|
285
|
+
Object.entries(context.diffs).forEach(([uid, diffs]) => {
|
|
286
|
+
for (const diff of diffs) {
|
|
287
|
+
const path = [uid].concat(diff.path).join('.');
|
|
288
|
+
const endPath = diff.path[diff.path.length - 1];
|
|
289
|
+
|
|
290
|
+
// Catch known features
|
|
291
|
+
if (
|
|
292
|
+
uid === 'admin::workflow' ||
|
|
293
|
+
uid === 'admin::workflow-stage' ||
|
|
294
|
+
endPath?.startsWith('strapi_reviewWorkflows_')
|
|
295
|
+
) {
|
|
296
|
+
workflowsStatus = diff.kind;
|
|
297
|
+
}
|
|
298
|
+
// handle generic cases
|
|
299
|
+
else if (diff.kind === 'added') {
|
|
300
|
+
engine.reportWarning(chalk.red(`${chalk.bold(path)} does not exist on source`), source);
|
|
301
|
+
} else if (diff.kind === 'deleted') {
|
|
302
|
+
engine.reportWarning(
|
|
303
|
+
chalk.red(`${chalk.bold(path)} does not exist on destination`),
|
|
304
|
+
source
|
|
305
|
+
);
|
|
306
|
+
} else if (diff.kind === 'modified') {
|
|
307
|
+
engine.reportWarning(chalk.red(`${chalk.bold(path)} has a different data type`), source);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
// output the known feature warnings
|
|
313
|
+
if (workflowsStatus === 'added') {
|
|
314
|
+
engine.reportWarning(chalk.red(`Review workflows feature does not exist on source`), source);
|
|
315
|
+
} else if (workflowsStatus === 'deleted') {
|
|
316
|
+
engine.reportWarning(
|
|
317
|
+
chalk.red(`Review workflows feature does not exist on destination`),
|
|
318
|
+
source
|
|
319
|
+
);
|
|
320
|
+
} else if (workflowsStatus === 'modified') {
|
|
321
|
+
engine.panic(
|
|
322
|
+
new TransferEngineInitializationError('Unresolved differences in schema [review workflows]')
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const confirmed = await confirmMessage(
|
|
327
|
+
'There are differences in schema between the source and destination, and the data listed above will be lost. Are you sure you want to continue?',
|
|
328
|
+
{
|
|
329
|
+
force,
|
|
330
|
+
}
|
|
331
|
+
);
|
|
332
|
+
|
|
333
|
+
// reset handler back to normal
|
|
334
|
+
setSignalHandler(() => abortTransfer({ engine, strapi }));
|
|
335
|
+
|
|
336
|
+
if (confirmed) {
|
|
337
|
+
context.ignoredDiffs = merge(context.diffs, context.ignoredDiffs);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
return next(context);
|
|
341
|
+
};
|
|
342
|
+
};
|
|
343
|
+
|
|
260
344
|
module.exports = {
|
|
261
345
|
loadersFactory,
|
|
262
346
|
buildTransferTable,
|
|
@@ -271,4 +355,6 @@ module.exports = {
|
|
|
271
355
|
validateExcludeOnly,
|
|
272
356
|
formatDiagnostic,
|
|
273
357
|
abortTransfer,
|
|
358
|
+
setSignalHandler,
|
|
359
|
+
getDiffHandler,
|
|
274
360
|
};
|
|
@@ -21,6 +21,14 @@ interface CustomFieldServerOptions {
|
|
|
21
21
|
* The existing Strapi data type the custom field uses
|
|
22
22
|
*/
|
|
23
23
|
type: string;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Settings for the input size in the Admin UI
|
|
27
|
+
*/
|
|
28
|
+
inputSize?: {
|
|
29
|
+
default: 4 | 6 | 8 | 12;
|
|
30
|
+
isResizable: boolean;
|
|
31
|
+
};
|
|
24
32
|
}
|
|
25
33
|
|
|
26
34
|
interface CustomFields {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@strapi/strapi",
|
|
3
|
-
"version": "4.10.
|
|
3
|
+
"version": "4.10.5",
|
|
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",
|
|
@@ -81,19 +81,19 @@
|
|
|
81
81
|
"dependencies": {
|
|
82
82
|
"@koa/cors": "3.4.3",
|
|
83
83
|
"@koa/router": "10.1.1",
|
|
84
|
-
"@strapi/admin": "4.10.
|
|
85
|
-
"@strapi/data-transfer": "4.10.
|
|
86
|
-
"@strapi/database": "4.10.
|
|
87
|
-
"@strapi/generate-new": "4.10.
|
|
88
|
-
"@strapi/generators": "4.10.
|
|
89
|
-
"@strapi/logger": "4.10.
|
|
90
|
-
"@strapi/permissions": "4.10.
|
|
91
|
-
"@strapi/plugin-content-manager": "4.10.
|
|
92
|
-
"@strapi/plugin-content-type-builder": "4.10.
|
|
93
|
-
"@strapi/plugin-email": "4.10.
|
|
94
|
-
"@strapi/plugin-upload": "4.10.
|
|
95
|
-
"@strapi/typescript-utils": "4.10.
|
|
96
|
-
"@strapi/utils": "4.10.
|
|
84
|
+
"@strapi/admin": "4.10.5",
|
|
85
|
+
"@strapi/data-transfer": "4.10.5",
|
|
86
|
+
"@strapi/database": "4.10.5",
|
|
87
|
+
"@strapi/generate-new": "4.10.5",
|
|
88
|
+
"@strapi/generators": "4.10.5",
|
|
89
|
+
"@strapi/logger": "4.10.5",
|
|
90
|
+
"@strapi/permissions": "4.10.5",
|
|
91
|
+
"@strapi/plugin-content-manager": "4.10.5",
|
|
92
|
+
"@strapi/plugin-content-type-builder": "4.10.5",
|
|
93
|
+
"@strapi/plugin-email": "4.10.5",
|
|
94
|
+
"@strapi/plugin-upload": "4.10.5",
|
|
95
|
+
"@strapi/typescript-utils": "4.10.5",
|
|
96
|
+
"@strapi/utils": "4.10.5",
|
|
97
97
|
"bcryptjs": "2.4.3",
|
|
98
98
|
"boxen": "5.1.2",
|
|
99
99
|
"chalk": "4.1.2",
|
|
@@ -117,7 +117,7 @@
|
|
|
117
117
|
"koa-compose": "4.1.0",
|
|
118
118
|
"koa-compress": "5.1.0",
|
|
119
119
|
"koa-favicon": "2.1.0",
|
|
120
|
-
"koa-helmet": "
|
|
120
|
+
"koa-helmet": "7.0.2",
|
|
121
121
|
"koa-ip": "^2.1.2",
|
|
122
122
|
"koa-session": "6.4.0",
|
|
123
123
|
"koa-static": "5.0.0",
|
|
@@ -142,5 +142,5 @@
|
|
|
142
142
|
"node": ">=14.19.1 <=18.x.x",
|
|
143
143
|
"npm": ">=6.0.0"
|
|
144
144
|
},
|
|
145
|
-
"gitHead": "
|
|
145
|
+
"gitHead": "d9277d616b4478a3839e79e47330a4aaf167a2f1"
|
|
146
146
|
}
|