ante-erp-cli 1.11.25 → 1.11.27
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/package.json +1 -1
- package/src/commands/clone-mongodb.js +17 -0
- package/src/utils/docker.js +12 -2
- package/src/utils/mongodb.js +67 -51
package/package.json
CHANGED
|
@@ -4,6 +4,7 @@ import inquirer from 'inquirer';
|
|
|
4
4
|
import { join } from 'path';
|
|
5
5
|
import { existsSync, readFileSync, readdirSync } from 'fs';
|
|
6
6
|
import { stat } from 'fs/promises';
|
|
7
|
+
import { execa } from 'execa';
|
|
7
8
|
import { getInstallDir } from '../utils/config.js';
|
|
8
9
|
import {
|
|
9
10
|
parseMongoUrl,
|
|
@@ -89,6 +90,22 @@ export async function cloneMongodb(sourceUrl, options = {}) {
|
|
|
89
90
|
const backupDir = join(installDir, 'backups', 'mongodb');
|
|
90
91
|
const composeFile = join(installDir, 'docker-compose.yml');
|
|
91
92
|
|
|
93
|
+
// Step 0: Ensure Docker image is available
|
|
94
|
+
console.log(chalk.yellow('Preparing Docker environment...'));
|
|
95
|
+
const imageSpinner = ora('Pulling MongoDB Docker image (this may take a few minutes on first run)...').start();
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
await execa('docker', ['pull', 'mongo:latest'], {
|
|
99
|
+
timeout: 300000 // 5 minutes for image pull
|
|
100
|
+
});
|
|
101
|
+
imageSpinner.succeed(chalk.green('Docker image ready'));
|
|
102
|
+
console.log('');
|
|
103
|
+
} catch (imageError) {
|
|
104
|
+
// Non-fatal: Image might already exist
|
|
105
|
+
imageSpinner.info(chalk.gray('Using existing Docker image'));
|
|
106
|
+
console.log('');
|
|
107
|
+
}
|
|
108
|
+
|
|
92
109
|
// Step 1: Parse source database URL
|
|
93
110
|
console.log(chalk.yellow('Step 1/5: Parsing source database URL...'));
|
|
94
111
|
let sourceInfo;
|
package/src/utils/docker.js
CHANGED
|
@@ -358,14 +358,24 @@ export async function runMigrations(composeFile) {
|
|
|
358
358
|
'deploy'
|
|
359
359
|
]);
|
|
360
360
|
|
|
361
|
+
// Combine stdout and stderr for complete output
|
|
362
|
+
const combinedOutput = [stdout, stderr].filter(Boolean).join('\n').trim();
|
|
363
|
+
|
|
361
364
|
return {
|
|
362
365
|
success: true,
|
|
363
|
-
output:
|
|
366
|
+
output: combinedOutput || 'Migration completed'
|
|
364
367
|
};
|
|
365
368
|
} catch (error) {
|
|
369
|
+
// Merge both stdout and stderr to show complete error messages
|
|
370
|
+
// This ensures error codes (like P3005) and resolution links are visible
|
|
371
|
+
const combinedError = [error.stdout, error.stderr]
|
|
372
|
+
.filter(Boolean)
|
|
373
|
+
.join('\n')
|
|
374
|
+
.trim();
|
|
375
|
+
|
|
366
376
|
return {
|
|
367
377
|
success: false,
|
|
368
|
-
output:
|
|
378
|
+
output: combinedError || error.message
|
|
369
379
|
};
|
|
370
380
|
}
|
|
371
381
|
}
|
package/src/utils/mongodb.js
CHANGED
|
@@ -68,71 +68,87 @@ export function buildMongoUrl(connectionInfo) {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
/**
|
|
71
|
-
* Test MongoDB connection using Docker
|
|
71
|
+
* Test MongoDB connection using Docker (with retry logic)
|
|
72
72
|
* @param {Object} connectionInfo - Connection details from parseMongoUrl
|
|
73
73
|
* @param {string} composeFile - Optional path to docker-compose.yml for Docker Compose network access
|
|
74
|
+
* @param {number} maxRetries - Maximum retry attempts (default: 2)
|
|
74
75
|
* @returns {Promise<{success: boolean, error?: string}>}
|
|
75
76
|
*/
|
|
76
|
-
export async function testConnection(connectionInfo, composeFile = null) {
|
|
77
|
-
|
|
78
|
-
const mongoUrl = buildMongoUrl(connectionInfo);
|
|
77
|
+
export async function testConnection(connectionInfo, composeFile = null, maxRetries = 2) {
|
|
78
|
+
let lastError = null;
|
|
79
79
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
'
|
|
87
|
-
|
|
80
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
81
|
+
try {
|
|
82
|
+
const mongoUrl = buildMongoUrl(connectionInfo);
|
|
83
|
+
|
|
84
|
+
// If composeFile provided, use Docker Compose exec (for local database in Docker network)
|
|
85
|
+
if (composeFile) {
|
|
86
|
+
await execa('docker', [
|
|
87
|
+
'compose',
|
|
88
|
+
'-f', composeFile,
|
|
89
|
+
'exec',
|
|
90
|
+
'-T',
|
|
91
|
+
'mongodb',
|
|
92
|
+
'mongosh',
|
|
93
|
+
mongoUrl,
|
|
94
|
+
'--quiet',
|
|
95
|
+
'--eval', 'db.adminCommand("ping")'
|
|
96
|
+
], {
|
|
97
|
+
timeout: 120000 // 120 second timeout (2 minutes) - allows for Docker image pull
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
return { success: true };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Standalone Docker for remote databases
|
|
104
|
+
let testUrl = mongoUrl;
|
|
105
|
+
const dockerArgs = ['run', '--rm'];
|
|
106
|
+
|
|
107
|
+
// Handle localhost connections
|
|
108
|
+
if (connectionInfo.host === 'localhost' || connectionInfo.host === '127.0.0.1') {
|
|
109
|
+
const protocol = connectionInfo.isSrv ? 'mongodb+srv://' : 'mongodb://';
|
|
110
|
+
const auth = connectionInfo.user && connectionInfo.password ?
|
|
111
|
+
`${connectionInfo.user}:${connectionInfo.password}@` : '';
|
|
112
|
+
const portPart = connectionInfo.port && !connectionInfo.isSrv ? `:${connectionInfo.port}` : '';
|
|
113
|
+
const authDbPart = connectionInfo.authDatabase && connectionInfo.authDatabase !== connectionInfo.database ?
|
|
114
|
+
`?authSource=${connectionInfo.authDatabase}` : '';
|
|
115
|
+
|
|
116
|
+
testUrl = `${protocol}${auth}host.docker.internal${portPart}/${connectionInfo.database}${authDbPart}`;
|
|
117
|
+
dockerArgs.push('--add-host=host.docker.internal:host-gateway');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
dockerArgs.push(
|
|
121
|
+
'mongo:latest',
|
|
88
122
|
'mongosh',
|
|
89
|
-
|
|
123
|
+
testUrl,
|
|
90
124
|
'--quiet',
|
|
91
125
|
'--eval', 'db.adminCommand("ping")'
|
|
92
|
-
|
|
93
|
-
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
// Use Docker to test connection
|
|
129
|
+
await execa('docker', dockerArgs, {
|
|
130
|
+
timeout: 120000 // 120 second timeout (2 minutes) - allows for Docker image pull
|
|
94
131
|
});
|
|
95
132
|
|
|
96
133
|
return { success: true };
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Standalone Docker for remote databases
|
|
100
|
-
let testUrl = mongoUrl;
|
|
101
|
-
const dockerArgs = ['run', '--rm'];
|
|
102
|
-
|
|
103
|
-
// Handle localhost connections
|
|
104
|
-
if (connectionInfo.host === 'localhost' || connectionInfo.host === '127.0.0.1') {
|
|
105
|
-
const protocol = connectionInfo.isSrv ? 'mongodb+srv://' : 'mongodb://';
|
|
106
|
-
const auth = connectionInfo.user && connectionInfo.password ?
|
|
107
|
-
`${connectionInfo.user}:${connectionInfo.password}@` : '';
|
|
108
|
-
const portPart = connectionInfo.port && !connectionInfo.isSrv ? `:${connectionInfo.port}` : '';
|
|
109
|
-
const authDbPart = connectionInfo.authDatabase && connectionInfo.authDatabase !== connectionInfo.database ?
|
|
110
|
-
`?authSource=${connectionInfo.authDatabase}` : '';
|
|
111
|
-
|
|
112
|
-
testUrl = `${protocol}${auth}host.docker.internal${portPart}/${connectionInfo.database}${authDbPart}`;
|
|
113
|
-
dockerArgs.push('--add-host=host.docker.internal:host-gateway');
|
|
114
|
-
}
|
|
134
|
+
} catch (error) {
|
|
135
|
+
lastError = error;
|
|
115
136
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
'--eval', 'db.adminCommand("ping")'
|
|
122
|
-
);
|
|
123
|
-
|
|
124
|
-
// Use Docker to test connection
|
|
125
|
-
await execa('docker', dockerArgs, {
|
|
126
|
-
timeout: 10000 // 10 second timeout
|
|
127
|
-
});
|
|
137
|
+
// If timeout and not last attempt, retry
|
|
138
|
+
if (error.timedOut && attempt < maxRetries) {
|
|
139
|
+
await new Promise(resolve => setTimeout(resolve, 2000)); // Wait 2s before retry
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
128
142
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
success: false,
|
|
133
|
-
error: error.message || 'Connection failed'
|
|
134
|
-
};
|
|
143
|
+
// If it's the last attempt or non-timeout error, break
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
135
146
|
}
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
success: false,
|
|
150
|
+
error: lastError?.message || 'Connection failed'
|
|
151
|
+
};
|
|
136
152
|
}
|
|
137
153
|
|
|
138
154
|
/**
|