1+ import { Command } from '@contentstack/cli-command' ;
2+ import { flags , FlagInput , cliux , log } from '@contentstack/cli-utilities' ;
3+ import { ImportRecoveryManager } from '../../../utils' ;
4+
5+ export default class ImportRecoveryCommand extends Command {
6+ static description = 'Analyze and recover from failed import operations' ;
7+
8+ static examples : string [ ] = [
9+ 'csdx cm:stacks:import-recovery --data-dir <path>' ,
10+ 'csdx cm:stacks:import-recovery --data-dir <path> --clean' ,
11+ 'csdx cm:stacks:import-recovery --data-dir <path> --report'
12+ ] ;
13+
14+ static flags : FlagInput = {
15+ 'data-dir' : flags . string ( {
16+ char : 'd' ,
17+ description : 'The path to the directory containing the import data and state files' ,
18+ required : true ,
19+ } ) ,
20+ clean : flags . boolean ( {
21+ description : 'Clean the import state to start fresh (creates backup)' ,
22+ default : false ,
23+ } ) ,
24+ report : flags . boolean ( {
25+ description : 'Generate a detailed recovery report' ,
26+ default : false ,
27+ } ) ,
28+ 'output-file' : flags . string ( {
29+ description : 'Save the recovery report to a file' ,
30+ } ) ,
31+ } ;
32+
33+ static usage : string = 'cm:stacks:import-recovery --data-dir <value> [--clean] [--report] [--output-file <value>]' ;
34+
35+ async run ( ) : Promise < void > {
36+ try {
37+ const { flags } = await this . parse ( ImportRecoveryCommand ) ;
38+
39+ const recoveryManager = ImportRecoveryManager . create ( flags [ 'data-dir' ] ) ;
40+
41+ if ( flags . clean ) {
42+ await this . cleanImportState ( recoveryManager ) ;
43+ return ;
44+ }
45+
46+ if ( flags . report ) {
47+ await this . generateReport ( recoveryManager , flags [ 'output-file' ] ) ;
48+ return ;
49+ }
50+
51+ // Default: analyze and provide recommendations
52+ await this . analyzeAndRecommend ( recoveryManager ) ;
53+
54+ } catch ( error ) {
55+ log . error ( `Recovery command failed: ${ error } ` ) ;
56+ cliux . print ( `Error: ${ error } ` , { color : 'red' } ) ;
57+ }
58+ }
59+
60+ private async analyzeAndRecommend ( recoveryManager : ImportRecoveryManager ) : Promise < void > {
61+ cliux . print ( '\n🔍 Analyzing import state...' , { color : 'blue' } ) ;
62+
63+ const info = recoveryManager . analyzeImportState ( ) ;
64+ const recommendation = recoveryManager . getRecoveryRecommendation ( info ) ;
65+
66+ // Display state information
67+ cliux . print ( '\n📊 Import State Summary:' , { color : 'cyan' } ) ;
68+ cliux . print ( ` State File: ${ info . stateFileExists ? '✅ Found' : '❌ Not Found' } ` ) ;
69+ cliux . print ( ` Assets Processed: ${ info . mappingCounts . assets } ` ) ;
70+ cliux . print ( ` Folders Processed: ${ info . mappingCounts . folders } ` ) ;
71+ cliux . print ( ` URL Mappings: ${ info . mappingCounts . urls } ` ) ;
72+
73+ if ( info . lastUpdated ) {
74+ const lastUpdated = new Date ( info . lastUpdated ) ;
75+ cliux . print ( ` Last Updated: ${ lastUpdated . toLocaleString ( ) } ` ) ;
76+ }
77+
78+ if ( info . estimatedProgress ) {
79+ cliux . print ( ` Estimated Progress: ~${ info . estimatedProgress } %` ) ;
80+ }
81+
82+ // Display recommendation
83+ cliux . print ( `\n💡 Recommendation: ${ recommendation . action . toUpperCase ( ) } ` , {
84+ color : recommendation . action === 'resume' ? 'green' :
85+ recommendation . action === 'restart' ? 'yellow' : 'red'
86+ } ) ;
87+ cliux . print ( ` ${ recommendation . reason } ` ) ;
88+
89+ if ( recommendation . commands && recommendation . commands . length > 0 ) {
90+ cliux . print ( '\n📋 Commands:' , { color : 'cyan' } ) ;
91+ recommendation . commands . forEach ( cmd => {
92+ if ( cmd . startsWith ( '#' ) ) {
93+ cliux . print ( ` ${ cmd } ` , { color : 'gray' } ) ;
94+ } else if ( cmd . trim ( ) === '' ) {
95+ cliux . print ( '' ) ;
96+ } else {
97+ cliux . print ( ` ${ cmd } ` , { color : 'white' } ) ;
98+ }
99+ } ) ;
100+ }
101+
102+ if ( recommendation . warnings && recommendation . warnings . length > 0 ) {
103+ cliux . print ( '\n⚠️ Warnings:' , { color : 'yellow' } ) ;
104+ recommendation . warnings . forEach ( warning => {
105+ cliux . print ( ` ${ warning } ` ) ;
106+ } ) ;
107+ }
108+
109+ cliux . print ( '\n💡 Tip: Use --report flag for a detailed analysis or --clean to start fresh\n' ) ;
110+ }
111+
112+ private async cleanImportState ( recoveryManager : ImportRecoveryManager ) : Promise < void > {
113+ cliux . print ( '\n🧹 Cleaning import state...' , { color : 'yellow' } ) ;
114+
115+ const info = recoveryManager . analyzeImportState ( ) ;
116+
117+ if ( ! info . stateFileExists ) {
118+ cliux . print ( '✅ No import state found. Nothing to clean.' , { color : 'green' } ) ;
119+ return ;
120+ }
121+
122+ if ( info . mappingCounts . assets > 0 || info . mappingCounts . folders > 0 ) {
123+ cliux . print ( `⚠️ Warning: This will remove progress for ${ info . mappingCounts . assets } assets and ${ info . mappingCounts . folders } folders.` , { color : 'yellow' } ) ;
124+
125+ const confirm = await cliux . confirm ( 'Are you sure you want to clean the import state? (y/N)' ) ;
126+ if ( ! confirm ) {
127+ cliux . print ( '❌ Operation cancelled.' , { color : 'red' } ) ;
128+ return ;
129+ }
130+ }
131+
132+ const success = recoveryManager . cleanImportState ( ) ;
133+
134+ if ( success ) {
135+ cliux . print ( '✅ Import state cleaned successfully. You can now start a fresh import.' , { color : 'green' } ) ;
136+ cliux . print ( '💡 A backup of the previous state has been created.' , { color : 'blue' } ) ;
137+ } else {
138+ cliux . print ( '❌ Failed to clean import state. Check the logs for details.' , { color : 'red' } ) ;
139+ }
140+ }
141+
142+ private async generateReport ( recoveryManager : ImportRecoveryManager , outputFile ?: string ) : Promise < void > {
143+ cliux . print ( '\n📄 Generating recovery report...' , { color : 'blue' } ) ;
144+
145+ const report = recoveryManager . generateRecoveryReport ( ) ;
146+
147+ if ( outputFile ) {
148+ try {
149+ const fs = require ( 'fs' ) ;
150+ fs . writeFileSync ( outputFile , report ) ;
151+ cliux . print ( `✅ Recovery report saved to: ${ outputFile } ` , { color : 'green' } ) ;
152+ } catch ( error ) {
153+ cliux . print ( `❌ Failed to save report to file: ${ error } ` , { color : 'red' } ) ;
154+ cliux . print ( '\n📄 Recovery Report:' , { color : 'cyan' } ) ;
155+ cliux . print ( report ) ;
156+ }
157+ } else {
158+ cliux . print ( '\n📄 Recovery Report:' , { color : 'cyan' } ) ;
159+ cliux . print ( report ) ;
160+ }
161+ }
162+ }
0 commit comments