genbox 1.0.0
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/dist/api.js +38 -0
- package/dist/commands/balance.js +21 -0
- package/dist/commands/connect.js +96 -0
- package/dist/commands/create.js +219 -0
- package/dist/commands/destroy.js +62 -0
- package/dist/commands/forward.js +166 -0
- package/dist/commands/help.js +404 -0
- package/dist/commands/init.js +488 -0
- package/dist/commands/list.js +36 -0
- package/dist/commands/login.js +56 -0
- package/dist/commands/push.js +191 -0
- package/dist/commands/restore-db.js +273 -0
- package/dist/commands/status.js +294 -0
- package/dist/commands/urls.js +74 -0
- package/dist/config-store.js +65 -0
- package/dist/config.js +162 -0
- package/dist/importer.js +103 -0
- package/dist/index.js +39 -0
- package/dist/scan.js +247 -0
- package/dist/schema.js +2 -0
- package/dist/ssh-config.js +216 -0
- package/package.json +63 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.pushCommand = void 0;
|
|
40
|
+
const commander_1 = require("commander");
|
|
41
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
42
|
+
const ora_1 = __importDefault(require("ora"));
|
|
43
|
+
const fs = __importStar(require("fs"));
|
|
44
|
+
const path = __importStar(require("path"));
|
|
45
|
+
const os = __importStar(require("os"));
|
|
46
|
+
const config_1 = require("../config");
|
|
47
|
+
const api_1 = require("../api");
|
|
48
|
+
function getPrivateSshKey() {
|
|
49
|
+
const home = os.homedir();
|
|
50
|
+
const potentialKeys = [
|
|
51
|
+
path.join(home, '.ssh', 'id_rsa'),
|
|
52
|
+
path.join(home, '.ssh', 'id_ed25519'),
|
|
53
|
+
];
|
|
54
|
+
for (const keyPath of potentialKeys) {
|
|
55
|
+
if (fs.existsSync(keyPath)) {
|
|
56
|
+
return fs.readFileSync(keyPath, 'utf-8');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
exports.pushCommand = new commander_1.Command('push')
|
|
62
|
+
.description('Upload workspace configuration to the cloud')
|
|
63
|
+
.option('--include-key', 'Include SSH private key for git cloning')
|
|
64
|
+
.option('--dry-run', 'Show what would be uploaded without actually uploading')
|
|
65
|
+
.action(async (options) => {
|
|
66
|
+
try {
|
|
67
|
+
// Check for config file
|
|
68
|
+
if (!(0, config_1.hasConfig)()) {
|
|
69
|
+
console.log(chalk_1.default.red('Error: genbox.yaml not found. Run "genbox init" first.'));
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
const config = (0, config_1.loadConfig)();
|
|
73
|
+
const envVars = (0, config_1.loadEnvVars)();
|
|
74
|
+
console.log(chalk_1.default.blue('Preparing workspace configuration...'));
|
|
75
|
+
console.log('');
|
|
76
|
+
// Build the payload
|
|
77
|
+
const payload = {
|
|
78
|
+
name: config.project_name,
|
|
79
|
+
displayName: config.project_name,
|
|
80
|
+
config: {
|
|
81
|
+
version: config.version,
|
|
82
|
+
system: config.system,
|
|
83
|
+
scripts: config.scripts,
|
|
84
|
+
hooks: config.hooks,
|
|
85
|
+
},
|
|
86
|
+
services: config.services,
|
|
87
|
+
repos: config.repos,
|
|
88
|
+
envVars: envVars,
|
|
89
|
+
};
|
|
90
|
+
// Process files to upload
|
|
91
|
+
if (config.files && config.files.length > 0) {
|
|
92
|
+
payload.files = [];
|
|
93
|
+
for (const f of config.files) {
|
|
94
|
+
const sourcePath = path.resolve(process.cwd(), f.source);
|
|
95
|
+
if (fs.existsSync(sourcePath)) {
|
|
96
|
+
const content = fs.readFileSync(sourcePath, 'utf-8');
|
|
97
|
+
payload.files.push({
|
|
98
|
+
path: f.target,
|
|
99
|
+
content: content,
|
|
100
|
+
permissions: f.permissions || '0644',
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
console.log(chalk_1.default.yellow(`Warning: File ${f.source} not found, skipping.`));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// Include SSH key if requested
|
|
109
|
+
if (options.includeKey) {
|
|
110
|
+
const privateKey = getPrivateSshKey();
|
|
111
|
+
if (privateKey) {
|
|
112
|
+
payload.privateKey = privateKey;
|
|
113
|
+
console.log(chalk_1.default.dim(' Including SSH private key'));
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
console.log(chalk_1.default.yellow('Warning: No SSH private key found in ~/.ssh/'));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// Display summary
|
|
120
|
+
console.log(chalk_1.default.bold('Workspace Configuration:'));
|
|
121
|
+
console.log(chalk_1.default.dim('───────────────────────────────────────────────'));
|
|
122
|
+
console.log(` ${chalk_1.default.bold('Name:')} ${payload.name}`);
|
|
123
|
+
console.log(` ${chalk_1.default.bold('Version:')} ${payload.config.version || '1.0'}`);
|
|
124
|
+
if (payload.services && Object.keys(payload.services).length > 0) {
|
|
125
|
+
console.log(` ${chalk_1.default.bold('Services:')} ${Object.keys(payload.services).join(', ')}`);
|
|
126
|
+
}
|
|
127
|
+
if (payload.repos && Object.keys(payload.repos).length > 0) {
|
|
128
|
+
console.log(` ${chalk_1.default.bold('Repos:')} ${Object.keys(payload.repos).join(', ')}`);
|
|
129
|
+
}
|
|
130
|
+
const envCount = Object.keys(envVars).length;
|
|
131
|
+
if (envCount > 0) {
|
|
132
|
+
console.log(` ${chalk_1.default.bold('Env Vars:')} ${envCount} variables`);
|
|
133
|
+
// Show keys only (not values for security)
|
|
134
|
+
console.log(chalk_1.default.dim(` ${Object.keys(envVars).slice(0, 5).join(', ')}${envCount > 5 ? '...' : ''}`));
|
|
135
|
+
}
|
|
136
|
+
if (payload.files && payload.files.length > 0) {
|
|
137
|
+
console.log(` ${chalk_1.default.bold('Files:')} ${payload.files.length} files`);
|
|
138
|
+
}
|
|
139
|
+
if (payload.privateKey) {
|
|
140
|
+
console.log(` ${chalk_1.default.bold('SSH Key:')} Included`);
|
|
141
|
+
}
|
|
142
|
+
console.log(chalk_1.default.dim('───────────────────────────────────────────────'));
|
|
143
|
+
console.log('');
|
|
144
|
+
// Dry run mode
|
|
145
|
+
if (options.dryRun) {
|
|
146
|
+
console.log(chalk_1.default.yellow('Dry run mode - no changes uploaded'));
|
|
147
|
+
console.log('');
|
|
148
|
+
console.log(chalk_1.default.dim('Payload preview (env values redacted):'));
|
|
149
|
+
const previewPayload = { ...payload };
|
|
150
|
+
if (previewPayload.envVars) {
|
|
151
|
+
previewPayload.envVars = Object.fromEntries(Object.keys(previewPayload.envVars).map(k => [k, '***']));
|
|
152
|
+
}
|
|
153
|
+
if (previewPayload.privateKey) {
|
|
154
|
+
previewPayload.privateKey = '***';
|
|
155
|
+
}
|
|
156
|
+
console.log(JSON.stringify(previewPayload, null, 2));
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
// Upload to server
|
|
160
|
+
const spinner = (0, ora_1.default)('Uploading workspace configuration...').start();
|
|
161
|
+
try {
|
|
162
|
+
const result = await (0, api_1.fetchApi)('/workspaces', {
|
|
163
|
+
method: 'PUT',
|
|
164
|
+
body: JSON.stringify(payload),
|
|
165
|
+
});
|
|
166
|
+
spinner.succeed(chalk_1.default.green('Workspace configuration uploaded successfully!'));
|
|
167
|
+
console.log('');
|
|
168
|
+
console.log(chalk_1.default.dim('Your configuration is now available in the cloud.'));
|
|
169
|
+
console.log(chalk_1.default.dim('Team members can run "genbox create <name>" to provision environments.'));
|
|
170
|
+
if (!(0, config_1.hasEnvFile)()) {
|
|
171
|
+
console.log('');
|
|
172
|
+
console.log(chalk_1.default.yellow('Note: No .env.genbox file found.'));
|
|
173
|
+
console.log(chalk_1.default.dim('Create one with sensitive environment variables that will be'));
|
|
174
|
+
console.log(chalk_1.default.dim('injected into your genboxes. Run "genbox init" to generate a template.'));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
spinner.fail(chalk_1.default.red('Failed to upload workspace configuration'));
|
|
179
|
+
console.error(chalk_1.default.red(`Error: ${error.message}`));
|
|
180
|
+
if (error.message.includes('401') || error.message.includes('Unauthorized')) {
|
|
181
|
+
console.log('');
|
|
182
|
+
console.log(chalk_1.default.yellow('You need to be logged in to push workspace configuration.'));
|
|
183
|
+
console.log(chalk_1.default.dim('Run "genbox login" to authenticate.'));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
console.error(chalk_1.default.red(`Error: ${error.message}`));
|
|
189
|
+
process.exit(1);
|
|
190
|
+
}
|
|
191
|
+
});
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.restoreDbCommand = void 0;
|
|
40
|
+
const commander_1 = require("commander");
|
|
41
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
42
|
+
const ora_1 = __importDefault(require("ora"));
|
|
43
|
+
const api_1 = require("../api");
|
|
44
|
+
const config_1 = require("../config");
|
|
45
|
+
const os = __importStar(require("os"));
|
|
46
|
+
const path = __importStar(require("path"));
|
|
47
|
+
const fs = __importStar(require("fs"));
|
|
48
|
+
const child_process_1 = require("child_process");
|
|
49
|
+
function getPrivateSshKey() {
|
|
50
|
+
const home = os.homedir();
|
|
51
|
+
const potentialKeys = [
|
|
52
|
+
path.join(home, '.ssh', 'id_rsa'),
|
|
53
|
+
path.join(home, '.ssh', 'id_ed25519'),
|
|
54
|
+
];
|
|
55
|
+
for (const keyPath of potentialKeys) {
|
|
56
|
+
if (fs.existsSync(keyPath)) {
|
|
57
|
+
return keyPath;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
throw new Error('No SSH private key found in ~/.ssh/');
|
|
61
|
+
}
|
|
62
|
+
function commandExists(cmd) {
|
|
63
|
+
try {
|
|
64
|
+
(0, child_process_1.execSync)(`which ${cmd}`, { stdio: 'pipe' });
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function getDatabaseSettings() {
|
|
72
|
+
// Defaults
|
|
73
|
+
let settings = {
|
|
74
|
+
localUri: 'mongodb://localhost:27017',
|
|
75
|
+
dbName: 'app',
|
|
76
|
+
container: 'mongodb',
|
|
77
|
+
};
|
|
78
|
+
// Try to load from config
|
|
79
|
+
if ((0, config_1.hasConfig)()) {
|
|
80
|
+
try {
|
|
81
|
+
const config = (0, config_1.loadConfig)();
|
|
82
|
+
if (config.database) {
|
|
83
|
+
if (config.database.localUri) {
|
|
84
|
+
settings.localUri = config.database.localUri;
|
|
85
|
+
}
|
|
86
|
+
if (config.database.name) {
|
|
87
|
+
settings.dbName = config.database.name;
|
|
88
|
+
}
|
|
89
|
+
if (config.database.container) {
|
|
90
|
+
settings.container = config.database.container;
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
// Default container name based on project name
|
|
94
|
+
settings.container = `${config.project_name}-mongodb`;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
// No database config, use project name for defaults
|
|
99
|
+
settings.dbName = config.project_name;
|
|
100
|
+
settings.container = `${config.project_name}-mongodb`;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
// Config load failed, use defaults
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// Override with env vars if present
|
|
108
|
+
const envVars = (0, config_1.loadEnvVars)();
|
|
109
|
+
if (envVars.MONGODB_URI) {
|
|
110
|
+
settings.localUri = envVars.MONGODB_URI;
|
|
111
|
+
}
|
|
112
|
+
return settings;
|
|
113
|
+
}
|
|
114
|
+
exports.restoreDbCommand = new commander_1.Command('restore-db')
|
|
115
|
+
.description('Restore local MongoDB database to a remote Genbox')
|
|
116
|
+
.argument('<name>', 'Name of the Genbox')
|
|
117
|
+
.option('--db <database>', 'Database name to restore (from genbox.yaml or project name)')
|
|
118
|
+
.option('--container <name>', 'Remote MongoDB container name (from genbox.yaml)')
|
|
119
|
+
.option('--local-uri <uri>', 'Local MongoDB URI (from genbox.yaml or MONGODB_URI env)')
|
|
120
|
+
.action(async (name, options) => {
|
|
121
|
+
const spinner = (0, ora_1.default)();
|
|
122
|
+
try {
|
|
123
|
+
// 1. Check prerequisites
|
|
124
|
+
if (!commandExists('mongodump')) {
|
|
125
|
+
console.error(chalk_1.default.red('mongodump not found. Please install MongoDB Database Tools:'));
|
|
126
|
+
console.log(chalk_1.default.dim(' brew install mongodb-database-tools'));
|
|
127
|
+
console.log(chalk_1.default.dim(' # or visit: https://www.mongodb.com/try/download/database-tools'));
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
// 2. Get database settings from config
|
|
131
|
+
const dbSettings = getDatabaseSettings();
|
|
132
|
+
const localUri = options.localUri || dbSettings.localUri;
|
|
133
|
+
const dbName = options.db || dbSettings.dbName;
|
|
134
|
+
const container = options.container || dbSettings.container;
|
|
135
|
+
console.log(chalk_1.default.dim(` Database: ${dbName}`));
|
|
136
|
+
console.log(chalk_1.default.dim(` Container: ${container}`));
|
|
137
|
+
console.log('');
|
|
138
|
+
// 3. Find Genbox
|
|
139
|
+
spinner.start('Finding Genbox...');
|
|
140
|
+
const genboxes = await (0, api_1.fetchApi)('/genboxes');
|
|
141
|
+
const target = genboxes.find((a) => a.name === name);
|
|
142
|
+
if (!target) {
|
|
143
|
+
spinner.fail(`Genbox '${name}' not found.`);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
if (!target.ipAddress) {
|
|
147
|
+
spinner.fail(`Genbox '${name}' is still provisioning (no IP). Please wait.`);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
spinner.succeed(`Found Genbox: ${name} (${target.ipAddress})`);
|
|
151
|
+
// 4. Get SSH key
|
|
152
|
+
let keyPath;
|
|
153
|
+
try {
|
|
154
|
+
keyPath = getPrivateSshKey();
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
console.error(chalk_1.default.red(error.message));
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
// 5. Test local MongoDB connection
|
|
161
|
+
spinner.start('Checking local MongoDB connection...');
|
|
162
|
+
try {
|
|
163
|
+
(0, child_process_1.execSync)(`mongosh "${localUri}" --quiet --eval "db.runCommand({ping:1})"`, {
|
|
164
|
+
stdio: 'pipe',
|
|
165
|
+
timeout: 10000,
|
|
166
|
+
});
|
|
167
|
+
spinner.succeed('Local MongoDB is accessible');
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
spinner.fail('Cannot connect to local MongoDB');
|
|
171
|
+
console.log(chalk_1.default.dim(`URI: ${localUri}`));
|
|
172
|
+
console.log(chalk_1.default.dim('Make sure your local MongoDB/Docker is running'));
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
// 6. Test remote MongoDB connection
|
|
176
|
+
spinner.start('Checking remote MongoDB...');
|
|
177
|
+
const sshOpts = `-i ${keyPath} -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR -o ConnectTimeout=10`;
|
|
178
|
+
try {
|
|
179
|
+
(0, child_process_1.execSync)(`ssh ${sshOpts} dev@${target.ipAddress} "docker exec ${container} mongosh --quiet --eval 'db.runCommand({ping:1})'"`, { stdio: 'pipe', timeout: 30000 });
|
|
180
|
+
spinner.succeed('Remote MongoDB is accessible');
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
spinner.fail('Cannot connect to remote MongoDB');
|
|
184
|
+
console.log(chalk_1.default.dim(`Container: ${container}`));
|
|
185
|
+
console.log(chalk_1.default.dim('Make sure the Genbox has completed setup and MongoDB container is running'));
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
// 7. Create dump from local MongoDB
|
|
189
|
+
const dumpDir = `/tmp/genbox-mongodump-${process.pid}`;
|
|
190
|
+
spinner.start(`Dumping local database '${dbName}'...`);
|
|
191
|
+
try {
|
|
192
|
+
(0, child_process_1.execSync)(`mongodump --uri="${localUri}" --db=${dbName} --out="${dumpDir}" --quiet`, {
|
|
193
|
+
stdio: 'pipe',
|
|
194
|
+
timeout: 300000, // 5 minutes
|
|
195
|
+
});
|
|
196
|
+
spinner.succeed(`Database dumped to ${dumpDir}`);
|
|
197
|
+
}
|
|
198
|
+
catch (error) {
|
|
199
|
+
spinner.fail('Failed to dump local database');
|
|
200
|
+
console.log(chalk_1.default.dim(error.message));
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
// 8. Compress the dump
|
|
204
|
+
spinner.start('Compressing dump...');
|
|
205
|
+
const archivePath = `${dumpDir}.tar.gz`;
|
|
206
|
+
try {
|
|
207
|
+
(0, child_process_1.execSync)(`tar -czf "${archivePath}" -C "${dumpDir}" ${dbName}`, { stdio: 'pipe' });
|
|
208
|
+
const stats = fs.statSync(archivePath);
|
|
209
|
+
const sizeMB = (stats.size / (1024 * 1024)).toFixed(2);
|
|
210
|
+
spinner.succeed(`Compressed: ${sizeMB} MB`);
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
spinner.fail('Failed to compress dump');
|
|
214
|
+
console.log(chalk_1.default.dim(error.message));
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
// 9. Upload to remote server
|
|
218
|
+
spinner.start('Uploading to Genbox...');
|
|
219
|
+
const remotePath = `/tmp/genbox-restore.tar.gz`;
|
|
220
|
+
try {
|
|
221
|
+
(0, child_process_1.execSync)(`scp ${sshOpts} "${archivePath}" dev@${target.ipAddress}:${remotePath}`, { stdio: 'pipe', timeout: 600000 } // 10 minutes
|
|
222
|
+
);
|
|
223
|
+
spinner.succeed('Upload complete');
|
|
224
|
+
}
|
|
225
|
+
catch (error) {
|
|
226
|
+
spinner.fail('Failed to upload dump');
|
|
227
|
+
console.log(chalk_1.default.dim(error.message));
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
// 10. Extract and restore on remote
|
|
231
|
+
spinner.start('Restoring database on Genbox...');
|
|
232
|
+
try {
|
|
233
|
+
const restoreCommands = `
|
|
234
|
+
cd /tmp && \
|
|
235
|
+
rm -rf /tmp/genbox-restore && \
|
|
236
|
+
mkdir -p /tmp/genbox-restore && \
|
|
237
|
+
tar -xzf ${remotePath} -C /tmp/genbox-restore && \
|
|
238
|
+
docker cp /tmp/genbox-restore/${dbName} ${container}:/tmp/ && \
|
|
239
|
+
docker exec ${container} mongorestore --drop --db=${dbName} /tmp/${dbName} && \
|
|
240
|
+
rm -rf /tmp/genbox-restore ${remotePath}
|
|
241
|
+
`;
|
|
242
|
+
(0, child_process_1.execSync)(`ssh ${sshOpts} dev@${target.ipAddress} "${restoreCommands}"`, { stdio: 'pipe', timeout: 600000 });
|
|
243
|
+
spinner.succeed('Database restored successfully!');
|
|
244
|
+
}
|
|
245
|
+
catch (error) {
|
|
246
|
+
spinner.fail('Failed to restore database');
|
|
247
|
+
console.log(chalk_1.default.dim(error.message));
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
// 11. Cleanup local files
|
|
251
|
+
try {
|
|
252
|
+
fs.rmSync(dumpDir, { recursive: true, force: true });
|
|
253
|
+
fs.unlinkSync(archivePath);
|
|
254
|
+
}
|
|
255
|
+
catch {
|
|
256
|
+
// Ignore cleanup errors
|
|
257
|
+
}
|
|
258
|
+
// 12. Show stats
|
|
259
|
+
console.log('');
|
|
260
|
+
console.log(chalk_1.default.blue('[INFO] Database Statistics:'));
|
|
261
|
+
try {
|
|
262
|
+
const statsOutput = (0, child_process_1.execSync)(`ssh ${sshOpts} dev@${target.ipAddress} "docker exec ${container} mongosh --quiet ${dbName} --eval 'print(\"Collections: \" + db.getCollectionNames().length); db.getCollectionNames().forEach(c => print(\" \" + c + \": \" + db[c].countDocuments()))'"`, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 30000 });
|
|
263
|
+
console.log(statsOutput);
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
266
|
+
console.log(chalk_1.default.dim(' Unable to fetch stats'));
|
|
267
|
+
}
|
|
268
|
+
console.log(chalk_1.default.green(`\n✓ Database '${dbName}' has been restored to ${name}`));
|
|
269
|
+
}
|
|
270
|
+
catch (error) {
|
|
271
|
+
spinner.fail(`Error: ${error.message}`);
|
|
272
|
+
}
|
|
273
|
+
});
|