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.
@@ -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
+ });