@strapi/strapi 4.6.0-alpha.0 → 4.6.0-alpha.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/bin/strapi.js CHANGED
@@ -7,18 +7,11 @@
7
7
  const _ = require('lodash');
8
8
  const resolveCwd = require('resolve-cwd');
9
9
  const { yellow } = require('chalk');
10
- const { Command, Option } = require('commander');
11
- const inquirer = require('inquirer');
10
+ const { Command } = require('commander');
12
11
 
13
12
  const program = new Command();
14
13
 
15
14
  const packageJSON = require('../package.json');
16
- const {
17
- parseInputList,
18
- parseInputBool,
19
- promptEncryptionKey,
20
- confirmKeyValue,
21
- } = require('../lib/commands/utils/commander');
22
15
 
23
16
  const checkCwdIsStrapiApp = (name) => {
24
17
  const logErrorAndExit = () => {
@@ -67,13 +60,6 @@ const getLocalScript =
67
60
  });
68
61
  };
69
62
 
70
- // option to exclude types of data for the export, import, and transfer commands
71
- // TODO: validate these inputs. Hopefully here, but worst case it may require adding a hook on each command
72
- const excludeOption = new Option(
73
- '--exclude <data,to,exclude>',
74
- 'Comma-separated list of data to exclude (files [localMediaFiles, providerMediaFiles], content [entities, links], schema, configuration)' // ['webhooks', 'content', 'localmedia', 'providermedia', 'relations']
75
- ).argParser(parseInputList);
76
-
77
63
  // Initial program setup
78
64
  program.storeOptionsAsProperties(false).allowUnknownOption(true);
79
65
 
@@ -269,97 +255,4 @@ program
269
255
  .option('-s, --silent', `Run the generation silently, without any output`, false)
270
256
  .action(getLocalScript('ts/generate-types'));
271
257
 
272
- // `$ strapi export`
273
- program
274
- .command('export')
275
- .description('Export data from Strapi to file')
276
- .addOption(
277
- new Option(
278
- '--encrypt <boolean>',
279
- `Encrypt output file using the 'aes-128-ecb' algorithm. Prompts for key unless key option is used.`
280
- )
281
- .default(true)
282
- .argParser(parseInputBool)
283
- )
284
- .addOption(
285
- new Option('--compress <boolean>', 'Compress output file using gzip compression')
286
- .default(true)
287
- .argParser(parseInputBool)
288
- )
289
- .addOption(
290
- new Option('--key <string>', 'Provide encryption key in command instead of using a prompt')
291
- )
292
- .addOption(
293
- new Option('--max-size <max MB per file>', 'split final file when exceeding size in MB')
294
- )
295
- .addOption(
296
- new Option(
297
- '--max-size-jsonl <max MB per internal backup file>',
298
- 'split internal jsonl files when exceeding max size in MB'
299
- )
300
- )
301
- .addOption(new Option('-f, --file <file>', 'name to use for exported file (without extensions)'))
302
- .addOption(excludeOption)
303
- .allowExcessArguments(false)
304
- .hook('preAction', promptEncryptionKey)
305
- .action(getLocalScript('transfer/export'));
306
-
307
- // `$ strapi import`
308
- program
309
- .command('import')
310
- .description('Import data from file to Strapi')
311
- .addOption(
312
- new Option('--conflictStrategy <conflictStrategy>', 'Which strategy to use for ID conflicts')
313
- .choices(['restore', 'abort', 'keep', 'replace'])
314
- .default('restore')
315
- )
316
- .addOption(excludeOption)
317
- .addOption(
318
- new Option(
319
- '--schemaComparison <schemaComparison>',
320
- 'exact requires every field to match, strict requires Strapi version and content type schema fields do not break, subset requires source schema to exist in destination, bypass skips checks',
321
- parseInputList
322
- )
323
- .choices(['exact', 'strict', 'subset', 'bypass'])
324
- .default('exact')
325
- )
326
- .requiredOption(
327
- '-f, --file <file>',
328
- 'path and filename to the Strapi export file you want to import'
329
- )
330
- .addOption(
331
- new Option('--key <string>', 'Provide encryption key in command instead of using a prompt')
332
- )
333
- .allowExcessArguments(false)
334
- .hook('preAction', async (thisCommand) => {
335
- const opts = thisCommand.opts();
336
-
337
- // check extension to guess if we should prompt for key
338
- if (String(opts.file).endsWith('.enc')) {
339
- if (!opts.key) {
340
- const answers = await inquirer.prompt([
341
- {
342
- type: 'password',
343
- message: 'Please enter your decryption key',
344
- name: 'key',
345
- },
346
- ]);
347
- if (!answers.key?.length) {
348
- console.log('No key entered, aborting import.');
349
- process.exit(0);
350
- }
351
- opts.key = answers.key;
352
- }
353
- }
354
- })
355
- .hook(
356
- 'preAction',
357
- confirmKeyValue(
358
- 'conflictStrategy',
359
- 'restore',
360
- "Using strategy 'restore' will delete all data in your database. Are you sure you want to proceed?"
361
- )
362
- )
363
- .action(getLocalScript('transfer/import'));
364
-
365
258
  program.parseAsync(process.argv);
package/lib/Strapi.js CHANGED
@@ -225,7 +225,7 @@ class Strapi {
225
225
 
226
226
  await this.runLifecyclesFunctions(LIFECYCLES.DESTROY);
227
227
 
228
- this.eventHub.removeAllListeners();
228
+ this.eventHub.destroy();
229
229
 
230
230
  if (_.has(this, 'db')) {
231
231
  await this.db.destroy();
@@ -54,7 +54,7 @@ const createContentType = (uid, definition) => {
54
54
  });
55
55
  } else {
56
56
  throw new Error(
57
- `Incorrect Content Type UID "${uid}". The UID should start with api::, plugin:: or strapi::.`
57
+ `Incorrect Content Type UID "${uid}". The UID should start with api::, plugin:: or admin::.`
58
58
  );
59
59
  }
60
60
 
@@ -134,17 +134,20 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator })
134
134
  // TODO: wrap into transaction
135
135
  const componentData = await createComponents(uid, validData);
136
136
 
137
+ const entityData = creationPipeline(
138
+ Object.assign(omitComponentData(model, validData), componentData),
139
+ {
140
+ contentType: model,
141
+ }
142
+ );
137
143
  let entity = await db.query(uid).create({
138
144
  ...query,
139
- data: creationPipeline(Object.assign(omitComponentData(model, validData), componentData), {
140
- contentType: model,
141
- }),
145
+ data: entityData,
142
146
  });
143
147
 
144
148
  // TODO: upload the files then set the links in the entity like with compo to avoid making too many queries
145
- // FIXME: upload in components
146
149
  if (files && Object.keys(files).length > 0) {
147
- await this.uploadFiles(uid, entity, files);
150
+ await this.uploadFiles(uid, Object.assign(entityData, entity), files);
148
151
  entity = await this.findOne(uid, entity.id, wrappedParams);
149
152
  }
150
153
 
@@ -180,19 +183,22 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator })
180
183
 
181
184
  // TODO: wrap in transaction
182
185
  const componentData = await updateComponents(uid, entityToUpdate, validData);
186
+ const entityData = updatePipeline(
187
+ Object.assign(omitComponentData(model, validData), componentData),
188
+ {
189
+ contentType: model,
190
+ }
191
+ );
183
192
 
184
193
  let entity = await db.query(uid).update({
185
194
  ...query,
186
195
  where: { id: entityId },
187
- data: updatePipeline(Object.assign(omitComponentData(model, validData), componentData), {
188
- contentType: model,
189
- }),
196
+ data: entityData,
190
197
  });
191
198
 
192
199
  // TODO: upload the files then set the links in the entity like with compo to avoid making too many queries
193
- // FIXME: upload in components
194
200
  if (files && Object.keys(files).length > 0) {
195
- await this.uploadFiles(uid, entity, files);
201
+ await this.uploadFiles(uid, Object.assign(entityData, entity), files);
196
202
  entity = await this.findOne(uid, entity.id, wrappedParams);
197
203
  }
198
204
 
@@ -1,16 +1,78 @@
1
+ 'use strict';
2
+
1
3
  /**
2
4
  * The event hub is Strapi's event control center.
3
5
  */
6
+ module.exports = function createEventHub() {
7
+ const listeners = new Map();
4
8
 
5
- 'use strict';
9
+ // Default subscriber to easily add listeners with the on() method
10
+ const defaultSubscriber = async (eventName, ...args) => {
11
+ if (listeners.has(eventName)) {
12
+ for (const listener of listeners.get(eventName)) {
13
+ await listener(...args);
14
+ }
15
+ }
16
+ };
6
17
 
7
- const EventEmitter = require('events');
18
+ // Store of subscribers that will be called when an event is emitted
19
+ const subscribers = [defaultSubscriber];
8
20
 
9
- class EventHub extends EventEmitter {}
21
+ const eventHub = {
22
+ async emit(eventName, ...args) {
23
+ for (const subscriber of subscribers) {
24
+ await subscriber(eventName, ...args);
25
+ }
26
+ },
10
27
 
11
- /**
12
- * Expose a factory function instead of the class
13
- */
14
- module.exports = function createEventHub(opts) {
15
- return new EventHub(opts);
28
+ subscribe(subscriber) {
29
+ subscribers.push(subscriber);
30
+
31
+ // Return a function to remove the subscriber
32
+ return () => {
33
+ eventHub.unsubscribe(subscriber);
34
+ };
35
+ },
36
+
37
+ unsubscribe(subscriber) {
38
+ subscribers.splice(subscribers.indexOf(subscriber), 1);
39
+ },
40
+
41
+ on(eventName, listener) {
42
+ if (!listeners.has(eventName)) {
43
+ listeners.set(eventName, []);
44
+ }
45
+
46
+ listeners.get(eventName).push(listener);
47
+
48
+ // Return a function to remove the listener
49
+ return () => {
50
+ eventHub.off(eventName, listener);
51
+ };
52
+ },
53
+
54
+ off(eventName, listener) {
55
+ listeners.get(eventName).splice(listeners.get(eventName).indexOf(listener), 1);
56
+ },
57
+
58
+ once(eventName, listener) {
59
+ return eventHub.on(eventName, async (...args) => {
60
+ eventHub.off(eventName, listener);
61
+ return listener(...args);
62
+ });
63
+ },
64
+
65
+ destroy() {
66
+ listeners.clear();
67
+ subscribers.length = 0;
68
+ return this;
69
+ },
70
+ };
71
+
72
+ return {
73
+ ...eventHub,
74
+ removeListener: eventHub.off,
75
+ removeAllListeners: eventHub.destroy,
76
+ addListener: eventHub.on,
77
+ };
16
78
  };
@@ -3,7 +3,7 @@ import { Attribute, ConfigurableOption, PrivateOption } from './base';
3
3
  import { GetAttributesByType, GetAttributesValues } from './utils';
4
4
 
5
5
  export type BasicRelationsType = 'oneToOne' | 'oneToMany' | 'manyToOne' | 'manyToMany';
6
- export type PolymorphicRelationsType = 'morphToOne' | 'morphToMany' | 'morphOne' | 'morphMany';
6
+ export type PolymorphicRelationsType = 'morphToOne' | 'morphToMany' | 'morphOne' | 'morphMany';
7
7
  export type RelationsType = BasicRelationsType | PolymorphicRelationsType;
8
8
 
9
9
  export interface BasicRelationAttributeProperties<
@@ -17,14 +17,16 @@ export interface BasicRelationAttributeProperties<
17
17
  mappedBy?: RelationsKeysFromTo<T, S>;
18
18
  }
19
19
 
20
- export interface PolymorphicRelationAttributeProperties<R extends RelationsType> {
20
+ export interface PolymorphicRelationAttributeProperties<
21
+ R extends RelationsType,
22
+ > {
21
23
  relation: R;
22
24
  }
23
25
 
24
26
  export type RelationAttribute<
25
27
  S extends SchemaUID,
26
28
  R extends RelationsType,
27
- T extends R extends PolymorphicRelationsType ? never : SchemaUID = never
29
+ T extends R extends PolymorphicRelationsType ? never: SchemaUID = never
28
30
  > = Attribute<'relation'> &
29
31
  // Properties
30
32
  (R extends BasicRelationsType
@@ -32,21 +34,22 @@ export type RelationAttribute<
32
34
  : PolymorphicRelationAttributeProperties<R>) &
33
35
  // Options
34
36
  ConfigurableOption &
35
- PrivateOption;
37
+ PrivateOption
36
38
 
37
39
  export type RelationsKeysFromTo<
38
40
  TTarget extends SchemaUID,
39
41
  TSource extends SchemaUID
40
42
  > = keyof PickRelationsFromTo<TTarget, TSource>;
41
43
 
42
- export type PickRelationsFromTo<
43
- TTarget extends SchemaUID,
44
- TSource extends SchemaUID
45
- > = GetAttributesByType<TTarget, 'relation', { target: TSource }>;
44
+ export type PickRelationsFromTo<TTarget extends SchemaUID, TSource extends SchemaUID> = GetAttributesByType<
45
+ TTarget,
46
+ 'relation',
47
+ { target: TSource }
48
+ >;
46
49
 
47
50
  export type RelationPluralityModifier<
48
51
  TRelation extends RelationsType,
49
- TValue extends Record<string, unknown>
52
+ TValue extends Object
50
53
  > = TRelation extends `${string}Many` ? TValue[] : TValue;
51
54
 
52
55
  export type RelationValue<
@@ -1,5 +1,5 @@
1
1
  import { Attribute, ComponentAttribute } from '../attributes';
2
- import { KeysBy, SchemaUID, StringRecord } from '../../utils';
2
+ import { KeysBy, StringRecord } from '../../utils';
3
3
 
4
4
  /**
5
5
  * Literal union type representing the possible natures of a content type
@@ -98,11 +98,6 @@ export interface PluginOptions {}
98
98
  export interface ContentTypeSchema extends Schema {
99
99
  modelType: 'contentType';
100
100
 
101
- /**
102
- * Unique identifier of the schema
103
- */
104
- uid: SchemaUID;
105
-
106
101
  /**
107
102
  * Determine the type of the content type (single-type or collection-type)
108
103
  */
@@ -2,8 +2,8 @@ import type Koa from 'koa';
2
2
  import { Database } from '@strapi/database';
3
3
 
4
4
  import type { StringMap } from './utils';
5
- import type { GenericController } from '../../../core-api/controller';
6
- import type { GenericService } from '../../../core-api/service';
5
+ import type { GenericController } from '../../../core-api/controller'
6
+ import type { GenericService } from '../../../core-api/service'
7
7
 
8
8
  // TODO move custom fields types to a separate file
9
9
  interface CustomFieldServerOptions {
@@ -92,16 +92,9 @@ export interface Strapi {
92
92
  */
93
93
  contentType(uid: string): any;
94
94
 
95
- /**
96
- * Getter for the Strapi component container
97
- *
98
- * It returns all the registered components
99
- */
100
- readonly components: any;
101
-
102
95
  /**
103
96
  * The custom fields registry
104
- *
97
+ *
105
98
  * It returns the custom fields interface
106
99
  */
107
100
  readonly customFields: CustomFields;
@@ -368,6 +361,7 @@ export interface Strapi {
368
361
  */
369
362
  log: any;
370
363
 
364
+
371
365
  /**
372
366
  * Used to manage cron within Strapi
373
367
  */
@@ -1,8 +1,8 @@
1
- import { Service, GenericService } from '../core-api/service';
1
+ import { Service,GenericService } from '../core-api/service';
2
2
  import { Controller, GenericController } from '../core-api/controller';
3
3
  import { Middleware } from '../middlewares';
4
4
  import { Policy } from '../core/registries/policies';
5
- import { Strapi } from './core/strapi';
5
+ import { Strapi } from '@strapi/strapi';
6
6
 
7
7
  type ControllerConfig<T extends Controller = Controller> = T;
8
8
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@strapi/strapi",
3
- "version": "4.6.0-alpha.0",
3
+ "version": "4.6.0-alpha.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,19 +80,18 @@
80
80
  "dependencies": {
81
81
  "@koa/cors": "3.4.3",
82
82
  "@koa/router": "10.1.1",
83
- "@strapi/admin": "4.6.0-alpha.0",
84
- "@strapi/data-transfer": "4.6.0-alpha.0",
85
- "@strapi/database": "4.6.0-alpha.0",
86
- "@strapi/generate-new": "4.6.0-alpha.0",
87
- "@strapi/generators": "4.6.0-alpha.0",
88
- "@strapi/logger": "4.6.0-alpha.0",
89
- "@strapi/permissions": "4.6.0-alpha.0",
90
- "@strapi/plugin-content-manager": "4.6.0-alpha.0",
91
- "@strapi/plugin-content-type-builder": "4.6.0-alpha.0",
92
- "@strapi/plugin-email": "4.6.0-alpha.0",
93
- "@strapi/plugin-upload": "4.6.0-alpha.0",
94
- "@strapi/typescript-utils": "4.6.0-alpha.0",
95
- "@strapi/utils": "4.6.0-alpha.0",
83
+ "@strapi/admin": "4.6.0-alpha.1",
84
+ "@strapi/database": "4.6.0-alpha.1",
85
+ "@strapi/generate-new": "4.6.0-alpha.1",
86
+ "@strapi/generators": "4.6.0-alpha.1",
87
+ "@strapi/logger": "4.6.0-alpha.1",
88
+ "@strapi/permissions": "4.6.0-alpha.1",
89
+ "@strapi/plugin-content-manager": "4.6.0-alpha.1",
90
+ "@strapi/plugin-content-type-builder": "4.6.0-alpha.1",
91
+ "@strapi/plugin-email": "4.6.0-alpha.1",
92
+ "@strapi/plugin-upload": "4.6.0-alpha.1",
93
+ "@strapi/typescript-utils": "4.6.0-alpha.1",
94
+ "@strapi/utils": "4.6.0-alpha.1",
96
95
  "bcryptjs": "2.4.3",
97
96
  "boxen": "5.1.2",
98
97
  "chalk": "4.1.2",
@@ -141,5 +140,5 @@
141
140
  "node": ">=14.19.1 <=18.x.x",
142
141
  "npm": ">=6.0.0"
143
142
  },
144
- "gitHead": "b7a87dcffc6f44e18eedef92e354096ffe32ce0c"
143
+ "gitHead": "9171c48104548f5f6da21abf2a8098009f1a40e9"
145
144
  }
@@ -1,166 +0,0 @@
1
- 'use strict';
2
-
3
- const {
4
- createLocalFileDestinationProvider,
5
- createLocalStrapiSourceProvider,
6
- createTransferEngine,
7
- // TODO: we need to solve this issue with typescript modules
8
- // eslint-disable-next-line import/no-unresolved, node/no-missing-require
9
- } = require('@strapi/data-transfer');
10
- const _ = require('lodash/fp');
11
- const Table = require('cli-table3');
12
- const fs = require('fs-extra');
13
-
14
- const chalk = require('chalk');
15
- const strapi = require('../../index');
16
- const { readableBytes } = require('../utils');
17
-
18
- const pad = (n) => {
19
- return (n < 10 ? '0' : '') + String(n);
20
- };
21
-
22
- const yyyymmddHHMMSS = () => {
23
- const date = new Date();
24
-
25
- return (
26
- date.getFullYear() +
27
- pad(date.getMonth() + 1) +
28
- pad(date.getDate()) +
29
- pad(date.getHours()) +
30
- pad(date.getMinutes()) +
31
- pad(date.getSeconds())
32
- );
33
- };
34
-
35
- const getDefaultExportName = () => {
36
- return `export_${yyyymmddHHMMSS()}`;
37
- };
38
-
39
- const logger = console;
40
-
41
- const BYTES_IN_MB = 1024 * 1024;
42
-
43
- module.exports = async (opts) => {
44
- // validate inputs from Commander
45
- if (!_.isObject(opts)) {
46
- logger.error('Could not parse arguments');
47
- process.exit(1);
48
- }
49
- const filename = opts.file;
50
-
51
- /**
52
- * From local Strapi instance
53
- */
54
- const sourceOptions = {
55
- async getStrapi() {
56
- return strapi(await strapi.compile()).load();
57
- },
58
- };
59
- const source = createLocalStrapiSourceProvider(sourceOptions);
60
-
61
- const file = _.isString(filename) && filename.length > 0 ? filename : getDefaultExportName();
62
-
63
- /**
64
- * To a Strapi backup file
65
- */
66
- // treat any unknown arguments as filenames
67
- const destinationOptions = {
68
- file: {
69
- path: file,
70
- maxSize: _.isFinite(opts.maxSize) ? Math.floor(opts.maxSize) * BYTES_IN_MB : undefined,
71
- maxSizeJsonl: _.isFinite(opts.maxSizeJsonl)
72
- ? Math.floor(opts.maxSizeJsonl) * BYTES_IN_MB
73
- : undefined,
74
- },
75
- encryption: {
76
- enabled: opts.encrypt,
77
- key: opts.key,
78
- },
79
- compression: {
80
- enabled: opts.compress,
81
- },
82
- };
83
- const destination = createLocalFileDestinationProvider(destinationOptions);
84
-
85
- /**
86
- * Configure and run the transfer engine
87
- */
88
- const engineOptions = {
89
- strategy: 'restore', // for an export to file, strategy will always be 'restore'
90
- versionMatching: 'ignore', // for an export to file, versionMatching will always be skipped
91
- exclude: opts.exclude,
92
- };
93
- const engine = createTransferEngine(source, destination, engineOptions);
94
-
95
- try {
96
- let resultData = [];
97
- logger.log(`Starting export...`);
98
-
99
- engine.progress.stream.on('start', ({ stage }) => {
100
- logger.log(`Starting transfer of ${stage}...`);
101
- });
102
-
103
- // engine.progress.stream..on('progress', ({ stage, data }) => {
104
- // logger.log('progress');
105
- // });
106
-
107
- engine.progress.stream.on('complete', ({ stage, data }) => {
108
- logger.log(`...${stage} complete`);
109
- resultData = data;
110
- });
111
-
112
- const results = await engine.transfer();
113
-
114
- // Build pretty table
115
- const table = new Table({
116
- head: ['Type', 'Count', 'Size'],
117
- });
118
-
119
- let totalBytes = 0;
120
- let totalItems = 0;
121
- Object.keys(resultData).forEach((key) => {
122
- const item = resultData[key];
123
-
124
- table.push([
125
- { hAlign: 'left', content: chalk.bold(key) },
126
- { hAlign: 'right', content: item.count },
127
- { hAlign: 'right', content: `${readableBytes(item.bytes, 1, 11)} ` },
128
- ]);
129
- totalBytes += item.bytes;
130
- totalItems += item.count;
131
-
132
- if (item.aggregates) {
133
- Object.keys(item.aggregates).forEach((subkey) => {
134
- const subitem = item.aggregates[subkey];
135
-
136
- table.push([
137
- { hAlign: 'left', content: `-- ${chalk.bold(subkey)}` },
138
- { hAlign: 'right', content: subitem.count },
139
- { hAlign: 'right', content: `(${chalk.grey(readableBytes(subitem.bytes, 1, 11))})` },
140
- ]);
141
- });
142
- }
143
- });
144
- table.push([
145
- { hAlign: 'left', content: chalk.bold.green('Total') },
146
- { hAlign: 'right', content: chalk.bold.green(totalItems) },
147
- { hAlign: 'right', content: `${chalk.bold.green(readableBytes(totalBytes, 1, 11))} ` },
148
- ]);
149
- logger.log(table.toString());
150
-
151
- // TODO: once archiving is implemented, we need to check file extensions
152
- if (!fs.pathExistsSync(results.destination.file.path)) {
153
- logger.log(file);
154
- throw new Error('Export file not created');
155
- }
156
-
157
- logger.log(`
158
- ${chalk.bold('Export process has been completed successfully!')}
159
- Export archive is in ${chalk.green(results.destination.file.path)}
160
- `);
161
- process.exit(0);
162
- } catch (e) {
163
- logger.error('Export process failed unexpectedly:', e.toString());
164
- process.exit(1);
165
- }
166
- };
@@ -1,65 +0,0 @@
1
- 'use strict';
2
-
3
- const {
4
- createLocalFileSourceProvider,
5
- createLocalStrapiDestinationProvider,
6
- createTransferEngine,
7
- // TODO: we need to solve this issue with typescript modules
8
- // eslint-disable-next-line import/no-unresolved, node/no-missing-require
9
- } = require('@strapi/data-transfer');
10
- const { isObject } = require('lodash/fp');
11
- const strapi = require('../../index');
12
-
13
- const logger = console;
14
-
15
- module.exports = async (opts) => {
16
- // validate inputs from Commander
17
- if (!isObject(opts)) {
18
- logger.error('Could not parse arguments');
19
- process.exit(1);
20
- }
21
- const filename = opts.file;
22
-
23
- /**
24
- * From strapi backup file
25
- */
26
-
27
- // treat any unknown arguments as filenames
28
- const sourceOptions = {
29
- backupFilePath: filename,
30
- };
31
- const source = createLocalFileSourceProvider(sourceOptions);
32
-
33
- /**
34
- * To local Strapi instance
35
- */
36
- const destinationOptions = {
37
- async getStrapi() {
38
- return strapi(await strapi.compile()).load();
39
- },
40
- };
41
- const destination = createLocalStrapiDestinationProvider(destinationOptions);
42
-
43
- /**
44
- * Configure and run the transfer engine
45
- */
46
- const engineOptions = {
47
- strategy: opts.conflictStrategy,
48
- versionMatching: opts.schemaComparison,
49
- exclude: opts.exclude,
50
- };
51
- const engine = createTransferEngine(source, destination, engineOptions);
52
-
53
- try {
54
- logger.log('Importing data...');
55
- const result = await engine.transfer();
56
- logger.log('Import process has been completed successfully!');
57
-
58
- // TODO: this won't dump the entire results, we will print a pretty summary
59
- logger.log('Results:', result);
60
- process.exit(0);
61
- } catch (e) {
62
- logger.log(`Import process failed unexpectedly: ${e.message}`);
63
- process.exit(1);
64
- }
65
- };
@@ -1,92 +0,0 @@
1
- 'use strict';
2
-
3
- const { parseType } = require('@strapi/utils/lib');
4
- const inquirer = require('inquirer');
5
-
6
- /**
7
- * argsParser: Parse a string argument from the command line as a boolean
8
- */
9
- const parseInputBool = (arg) => {
10
- try {
11
- return parseType({ type: 'boolean', value: arg });
12
- } catch (e) {
13
- console.error(e.message);
14
- process.exit(1);
15
- }
16
- };
17
-
18
- /**
19
- * argsParser: Parse a comma-delimited string as an array
20
- */
21
- const parseInputList = (value) => {
22
- return value.split(',');
23
- };
24
-
25
- /**
26
- * hook: if encrpyt=true and key not provided, prompt for it
27
- */
28
- const promptEncryptionKey = async (thisCommand) => {
29
- const opts = thisCommand.opts();
30
-
31
- if (!opts.encrypt && opts.key) {
32
- console.error('Key may not be present unless encryption is used');
33
- process.exit(1);
34
- }
35
-
36
- // if encrypt is set but we have no key, prompt for it
37
- if (opts.encrypt && !(opts.key && opts.key.length > 0)) {
38
- try {
39
- const answers = await inquirer.prompt([
40
- {
41
- type: 'password',
42
- message: 'Please enter an encryption key',
43
- name: 'key',
44
- validate(key) {
45
- if (key.length > 0) return true;
46
-
47
- return 'Key must be present when using the encrypt option';
48
- },
49
- },
50
- ]);
51
- opts.key = answers.key;
52
- } catch (e) {
53
- console.error('Failed to get encryption key');
54
- process.exit(1);
55
- }
56
- if (!opts.key) {
57
- console.error('Failed to get encryption key');
58
- process.exit(1);
59
- }
60
- }
61
- };
62
-
63
- /**
64
- * hook: confirm that key has a value with a provided message
65
- */
66
- const confirmKeyValue = (key, value, message) => {
67
- return async (thisCommand) => {
68
- const opts = thisCommand.opts();
69
-
70
- if (!opts[key] || opts[key] !== value) {
71
- console.error(`Could not confirm key ${key}, halting operation.`);
72
- process.exit(1);
73
- }
74
- const answers = await inquirer.prompt([
75
- {
76
- type: 'confirm',
77
- message,
78
- name: `confirm_${key}`,
79
- },
80
- ]);
81
- if (!answers[`confirm_${key}`]) {
82
- process.exit(0);
83
- }
84
- };
85
- };
86
-
87
- module.exports = {
88
- parseInputList,
89
- parseInputBool,
90
- promptEncryptionKey,
91
- confirmKeyValue,
92
- };
@@ -1,20 +0,0 @@
1
- 'use strict';
2
-
3
- const bytesPerKb = 1024;
4
- const sizes = ['B ', 'KB', 'MB', 'GB', 'TB', 'PB'];
5
-
6
- const readableBytes = (bytes, decimals = 1, padStart = 0) => {
7
- if (!bytes) {
8
- return '0';
9
- }
10
- const i = Math.floor(Math.log(bytes) / Math.log(bytesPerKb));
11
- const result = `${parseFloat((bytes / bytesPerKb ** i).toFixed(decimals))} ${sizes[i].padStart(
12
- 2
13
- )}`;
14
-
15
- return result.padStart(padStart);
16
- };
17
-
18
- module.exports = {
19
- readableBytes,
20
- };