UNPKG

@strapi/strapi

Version:

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

241 lines (237 loc) 9.9 kB
'use strict'; var fp = require('lodash/fp'); var ora = require('ora'); var dataTransfer = require('@strapi/data-transfer'); var dataTransfer$1 = require('../../utils/data-transfer.js'); var helpers = require('../../utils/helpers.js'); const { createTransferEngine } = dataTransfer.engine; const { providers: { createRemoteStrapiDestinationProvider, createLocalStrapiSourceProvider, createLocalStrapiDestinationProvider, createRemoteStrapiSourceProvider } } = dataTransfer.strapi; const resolveRemotePullAssetIdleTimeoutMs = (value)=>{ if (value == null || value === '') { return undefined; } const n = typeof value === 'number' ? value : Number(value); if (!Number.isFinite(n) || n <= 0) { return undefined; } return n; }; /** * Transfer command. * * Transfers data between local Strapi and remote Strapi instances */ var action = (async (opts)=>{ // Avoid DeprecationWarning lines on stderr (e.g. pg `client.query()` while a query is in flight) // interleaving with ora spinners during transfer. (Runtime API; not on all @types/node Process typings.) process.noDeprecation = true; // Validate inputs from Commander if (!fp.isObject(opts)) { helpers.exitWith(1, 'Could not parse command arguments'); } if (!(opts.from || opts.to) || opts.from && opts.to) { helpers.exitWith(1, 'Exactly one source (from) or destination (to) option must be provided'); } const strapi = await dataTransfer$1.createStrapiInstance(); const checksumsEnabled = opts.checksums !== false; let source; let destination; // if no URL provided, use local Strapi if (!opts.from) { source = createLocalStrapiSourceProvider({ getStrapi: ()=>strapi }); } else { if (!opts.fromToken) { helpers.exitWith(1, 'Missing token for remote destination'); } const assetIdleTimeoutMs = resolveRemotePullAssetIdleTimeoutMs(strapi.config.get('server.transfer.remote.assetIdleTimeoutMs')); source = createRemoteStrapiSourceProvider({ getStrapi: ()=>strapi, url: opts.from, auth: { type: 'token', token: opts.fromToken }, ...assetIdleTimeoutMs !== undefined ? { streamTimeout: assetIdleTimeoutMs } : {}, ...checksumsEnabled ? { verifyChecksums: true } : {} }); } /** Wired after `engine` exists so destination prep can update the CLI spinner. */ const transferPhaseBridge = { emit () { /* replaced below once `progress` exists */ } }; // if no URL provided, use local Strapi if (!opts.to) { destination = createLocalStrapiDestinationProvider({ getStrapi: ()=>strapi, strategy: 'restore', restore: dataTransfer$1.parseRestoreFromOptions(opts, strapi), onTransferPhase: (message)=>transferPhaseBridge.emit(message) }); } else { if (!opts.toToken) { helpers.exitWith(1, 'Missing token for remote destination'); } destination = createRemoteStrapiDestinationProvider({ url: opts.to, auth: { type: 'token', token: opts.toToken }, strategy: 'restore', restore: dataTransfer$1.parseRestoreFromOptions(opts, strapi), onTransferPhase: (message)=>transferPhaseBridge.emit(message), ...checksumsEnabled ? { verifyChecksums: true } : {} }); } if (!source || !destination) { helpers.exitWith(1, 'Could not create providers'); } const engine = createTransferEngine(source, destination, { versionStrategy: 'exact', schemaStrategy: 'strict', exclude: opts.exclude, only: opts.only, throttle: opts.throttle, transforms: { links: [ { filter (link) { return !dataTransfer$1.isIgnoredContentType(link.left.type) && !dataTransfer$1.isIgnoredContentType(link.right.type); } } ], entities: [ { filter (entity) { return !dataTransfer$1.isIgnoredContentType(entity.type); } } ] } }); engine.diagnostics.onDiagnostic(dataTransfer$1.formatDiagnostic('transfer', opts.verbose)); const progress = engine.progress.stream; /** Shown until destination prep emits a step; then we keep this prefix and append the step after " — ". */ const STARTING_TRANSFER_PREFIX = 'Starting transfer…'; let prepStepDetail = null; const formatPrepSpinnerLine = ()=>prepStepDetail != null && prepStepDetail !== '' ? `${STARTING_TRANSFER_PREFIX}${prepStepDetail}` : STARTING_TRANSFER_PREFIX; transferPhaseBridge.emit = (message)=>{ prepStepDetail = message; progress.emit('transfer::phase', { message: formatPrepSpinnerLine() }); }; const { updateLoader } = dataTransfer$1.loadersFactory(); let startingSpinner = null; let startingElapsedInterval = null; /** Set when `transfer::start` fires so we can print a final persisted line with elapsed time. */ let transferPrepStartedAt = null; /** * Stops the "starting transfer" spinner and **leaves a finished line** in the console (like stage * `succeed`/`fail`), so the next stage spinner starts on a new line instead of replacing this one. */ const finishStartingSpinner = (outcome = 'done')=>{ if (startingElapsedInterval) { clearInterval(startingElapsedInterval); startingElapsedInterval = null; } if (startingSpinner) { const elapsed = transferPrepStartedAt != null ? Date.now() - transferPrepStartedAt : 0; const line = `${formatPrepSpinnerLine()}${helpers.TRANSFER_PROGRESS_FIELD_SEP}${helpers.formatElapsedAndMaybeRemainingLabel(elapsed, null)}`; if (outcome === 'fail') { startingSpinner.fail(line); } else { startingSpinner.succeed(line); } startingSpinner = null; transferPrepStartedAt = null; } }; engine.onSchemaDiff(dataTransfer$1.getDiffHandler(engine, { force: opts.force, action: 'transfer' })); engine.addErrorHandler('ASSETS_DIRECTORY_ERR', dataTransfer$1.getAssetsBackupHandler(engine, { force: opts.force, action: 'transfer' })); // Update more frequently to ensure elapsed time is accurate even if the stage is not progressing const activeStages = new Set(); const lastStageData = {}; const interval = setInterval(()=>{ for (const stage of activeStages){ if (lastStageData[stage]) { // Clone the lastStageData and ensure endTime is undefined so elapsed uses Date.now() const dataCopy = { ...lastStageData[stage], endTime: undefined }; updateLoader(stage, { [stage]: dataCopy }); } } }, 100); progress.on(`stage::start`, ({ stage, data })=>{ finishStartingSpinner('done'); updateLoader(stage, data).start(); }); progress.on('stage::finish', ({ stage, data })=>{ updateLoader(stage, data).succeed(); }); progress.on('stage::progress', ({ stage, data })=>{ lastStageData[stage] = data[stage]; activeStages.add(stage); updateLoader(stage, data); }); progress.on('stage::error', ({ stage, data })=>{ updateLoader(stage, data).fail(); }); progress.on('transfer::finish', ()=>{ finishStartingSpinner('done'); clearInterval(interval); }); progress.on('transfer::error', ()=>{ finishStartingSpinner('fail'); clearInterval(interval); }); progress.on('transfer::start', async ()=>{ transferPrepStartedAt = Date.now(); prepStepDetail = null; startingSpinner = ora(formatPrepSpinnerLine()).start(); startingElapsedInterval = setInterval(()=>{ if (startingSpinner && transferPrepStartedAt != null) { const elapsed = Date.now() - transferPrepStartedAt; startingSpinner.text = `${formatPrepSpinnerLine()}${helpers.TRANSFER_PROGRESS_FIELD_SEP}${helpers.formatElapsedAndMaybeRemainingLabel(elapsed, null)}`; } }, 100); await strapi.telemetry.send('didDEITSProcessStart', dataTransfer$1.getTransferTelemetryPayload(engine)); }); let results; try { // Abort transfer if user interrupts process dataTransfer$1.setSignalHandler(()=>dataTransfer$1.abortTransfer({ engine, strapi })); results = await engine.transfer(); // Note: we need to await telemetry or else the process ends before it is sent await strapi.telemetry.send('didDEITSProcessFinish', dataTransfer$1.getTransferTelemetryPayload(engine)); try { const table = dataTransfer$1.buildTransferTable(results.engine); console.log(table?.toString()); } catch (e) { console.error('There was an error displaying the results of the transfer.'); } helpers.exitWith(0, dataTransfer$1.exitMessageText('transfer')); } catch (e) { await strapi.telemetry.send('didDEITSProcessFail', dataTransfer$1.getTransferTelemetryPayload(engine)); helpers.exitWith(1, dataTransfer$1.exitMessageText('transfer', true)); } }); module.exports = action; //# sourceMappingURL=action.js.map