@lenne.tech/cli 0.0.123 → 0.0.125

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,472 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __asyncValues = (this && this.__asyncValues) || function (o) {
12
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
13
+ var m = o[Symbol.asyncIterator], i;
14
+ return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
15
+ function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
16
+ function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
17
+ };
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ const client_s3_1 = require("@aws-sdk/client-s3");
20
+ const fs = require("fs");
21
+ const path = require("path");
22
+ const util_1 = require("util");
23
+ const execAsync = (0, util_1.promisify)(require('child_process').exec);
24
+ /**
25
+ * Restore MongoDB database from S3 backup
26
+ */
27
+ const command = {
28
+ alias: ['s3r'],
29
+ description: 'Restore MongoDB database from S3 backup',
30
+ name: 's3-restore',
31
+ run: (toolbox) => __awaiter(void 0, void 0, void 0, function* () {
32
+ var _a, e_1, _b, _c;
33
+ const { helper, parameters, print: { error, info, spin, success, warning }, prompt, system, } = toolbox;
34
+ // Start timer
35
+ const timer = system.startTimer();
36
+ info('MongoDB Restore from S3');
37
+ info('');
38
+ // ============================================================================
39
+ // Step 1: S3 Configuration
40
+ // ============================================================================
41
+ info('Step 1: S3 Configuration');
42
+ info('');
43
+ const s3Bucket = yield helper.getInput(parameters.options.bucket || process.env.S3_BUCKET, {
44
+ name: 'S3 Bucket Name',
45
+ showError: true,
46
+ });
47
+ if (!s3Bucket) {
48
+ error('S3 Bucket is required');
49
+ return;
50
+ }
51
+ const s3Key = yield helper.getInput(parameters.options.key || process.env.S3_KEY, {
52
+ name: 'S3 Access Key ID',
53
+ showError: true,
54
+ });
55
+ if (!s3Key) {
56
+ error('S3 Access Key ID is required');
57
+ return;
58
+ }
59
+ const s3Secret = yield helper.getInput(parameters.options.secret || process.env.S3_SECRET, {
60
+ name: 'S3 Secret Access Key',
61
+ showError: true,
62
+ });
63
+ if (!s3Secret) {
64
+ error('S3 Secret Access Key is required');
65
+ return;
66
+ }
67
+ const s3Url = yield helper.getInput(parameters.options.url || process.env.S3_URL, {
68
+ name: 'S3 Endpoint URL',
69
+ showError: true,
70
+ });
71
+ if (!s3Url) {
72
+ error('S3 Endpoint URL is required');
73
+ return;
74
+ }
75
+ const s3Region = yield helper.getInput(parameters.options.region || process.env.S3_REGION || 'de', {
76
+ initial: 'de',
77
+ name: 'S3 Region (optional)',
78
+ showError: false,
79
+ });
80
+ const s3Folder = yield helper.getInput(parameters.options.folder || process.env.S3_FOLDER || '', {
81
+ initial: '',
82
+ name: 'S3 Folder/Prefix (optional)',
83
+ showError: false,
84
+ });
85
+ // ============================================================================
86
+ // Step 2: Initialize S3 Client and List Backups
87
+ // ============================================================================
88
+ info('');
89
+ info('Step 2: Fetching available backups from S3...');
90
+ info('');
91
+ let s3Client;
92
+ let backupFiles = [];
93
+ try {
94
+ s3Client = new client_s3_1.S3({
95
+ credentials: {
96
+ accessKeyId: s3Key,
97
+ secretAccessKey: s3Secret,
98
+ },
99
+ endpoint: s3Url,
100
+ forcePathStyle: true,
101
+ region: s3Region,
102
+ });
103
+ const listSpin = spin('Listing backup files from S3');
104
+ // List all backup files from S3
105
+ const paginator = (0, client_s3_1.paginateListObjectsV2)({ client: s3Client }, {
106
+ Bucket: s3Bucket,
107
+ Prefix: s3Folder,
108
+ });
109
+ try {
110
+ for (var _d = true, paginator_1 = __asyncValues(paginator), paginator_1_1; paginator_1_1 = yield paginator_1.next(), _a = paginator_1_1.done, !_a; _d = true) {
111
+ _c = paginator_1_1.value;
112
+ _d = false;
113
+ const page = _c;
114
+ const objects = page.Contents;
115
+ if (!(objects === null || objects === void 0 ? void 0 : objects.length)) {
116
+ continue;
117
+ }
118
+ backupFiles = [
119
+ ...backupFiles,
120
+ ...objects
121
+ .filter(object => object.Key && (object.Key.endsWith('.tar.gz') || object.Key.endsWith('.archive')))
122
+ .map(object => ({
123
+ key: object.Key,
124
+ label: object.Key.split('/').pop() || object.Key,
125
+ lastModified: object.LastModified,
126
+ size: object.Size,
127
+ })),
128
+ ];
129
+ }
130
+ }
131
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
132
+ finally {
133
+ try {
134
+ if (!_d && !_a && (_b = paginator_1.return)) yield _b.call(paginator_1);
135
+ }
136
+ finally { if (e_1) throw e_1.error; }
137
+ }
138
+ // Sort by last modified date (newest first)
139
+ backupFiles.sort((a, b) => {
140
+ if (!a.lastModified || !b.lastModified) {
141
+ return 0;
142
+ }
143
+ return b.lastModified.getTime() - a.lastModified.getTime();
144
+ });
145
+ listSpin.succeed(`Found ${backupFiles.length} backup file(s)`);
146
+ }
147
+ catch (err) {
148
+ error(`Failed to connect to S3 or list backups: ${err.message}`);
149
+ return;
150
+ }
151
+ if (backupFiles.length === 0) {
152
+ warning('No backup files found in the specified S3 bucket/folder');
153
+ return;
154
+ }
155
+ // ============================================================================
156
+ // Step 3: Select Backup File
157
+ // ============================================================================
158
+ info('');
159
+ info('Step 3: Select backup file');
160
+ info('');
161
+ // Format backup files for selection with additional info
162
+ const backupChoices = backupFiles.map((file, index) => {
163
+ const sizeStr = file.size ? `${(file.size / 1024 / 1024).toFixed(2)} MB` : 'Unknown size';
164
+ const dateStr = file.lastModified
165
+ ? file.lastModified.toLocaleString('de-DE', {
166
+ day: '2-digit',
167
+ hour: '2-digit',
168
+ minute: '2-digit',
169
+ month: '2-digit',
170
+ year: 'numeric',
171
+ })
172
+ : 'Unknown date';
173
+ const marker = index === 0 ? ' (newest)' : '';
174
+ return {
175
+ message: `${file.label}${marker} - ${dateStr} - ${sizeStr}`,
176
+ name: file.key,
177
+ };
178
+ });
179
+ const { selectedBackupKey } = yield prompt.ask({
180
+ choices: backupChoices,
181
+ initial: 0, // Pre-select the newest (first) backup
182
+ message: 'Select backup file to restore:',
183
+ name: 'selectedBackupKey',
184
+ type: 'select',
185
+ });
186
+ if (!selectedBackupKey) {
187
+ error('No backup file selected');
188
+ return;
189
+ }
190
+ const selectedBackup = backupFiles.find(f => f.key === selectedBackupKey);
191
+ if (!selectedBackup) {
192
+ error('Selected backup not found');
193
+ return;
194
+ }
195
+ success(`Selected: ${selectedBackup.label}`);
196
+ // ============================================================================
197
+ // Step 4: Download Backup
198
+ // ============================================================================
199
+ info('');
200
+ info('Step 4: Downloading backup...');
201
+ info('');
202
+ const tempDir = path.join('/tmp', `backup-${Date.now()}`);
203
+ const backupFile = path.join(tempDir, 'backup.archive');
204
+ try {
205
+ yield fs.promises.mkdir(tempDir, { recursive: true });
206
+ const downloadSpin = spin(`Downloading ${selectedBackup.label}`);
207
+ const command = {
208
+ Bucket: s3Bucket,
209
+ Key: selectedBackup.key,
210
+ };
211
+ const data = yield s3Client.getObject(command);
212
+ const bodyStream = data.Body;
213
+ const bodyArray = yield bodyStream.transformToByteArray();
214
+ yield fs.promises.writeFile(backupFile, Buffer.from(bodyArray));
215
+ downloadSpin.succeed(`Downloaded ${selectedBackup.label} (${(bodyArray.length / 1024 / 1024).toFixed(2)} MB)`);
216
+ }
217
+ catch (err) {
218
+ error(`Failed to download backup: ${err.message}`);
219
+ // Cleanup
220
+ try {
221
+ yield fs.promises.rm(tempDir, { force: true, recursive: true });
222
+ }
223
+ catch (e) {
224
+ // Ignore cleanup errors
225
+ }
226
+ return;
227
+ }
228
+ // ============================================================================
229
+ // Step 5: Extract Backup
230
+ // ============================================================================
231
+ info('');
232
+ info('Step 5: Extracting backup...');
233
+ info('');
234
+ const extractDir = path.join(tempDir, 'extracted');
235
+ try {
236
+ yield fs.promises.mkdir(extractDir, { recursive: true });
237
+ const extractSpin = spin('Extracting backup archive');
238
+ const extractCommand = `tar -xzf "${backupFile}" -C "${extractDir}"`;
239
+ yield execAsync(extractCommand);
240
+ extractSpin.succeed('Backup extracted');
241
+ }
242
+ catch (err) {
243
+ error(`Failed to extract backup: ${err.message}`);
244
+ // Cleanup
245
+ try {
246
+ yield fs.promises.rm(tempDir, { force: true, recursive: true });
247
+ }
248
+ catch (e) {
249
+ // Ignore cleanup errors
250
+ }
251
+ return;
252
+ }
253
+ // ============================================================================
254
+ // Step 6: Find Databases in Backup
255
+ // ============================================================================
256
+ info('');
257
+ info('Step 6: Detecting databases from backup...');
258
+ info('');
259
+ let backupRootDir = '';
260
+ let sourceDbName = '';
261
+ const systemDatabases = ['admin', 'local', 'config'];
262
+ try {
263
+ const findDbSpin = spin('Searching for database files');
264
+ // Find all directories containing BSON files
265
+ const findBsonCommand = `find "${extractDir}" -name "*.bson" -exec dirname {} \\; | sort -u`;
266
+ const { stdout } = yield execAsync(findBsonCommand);
267
+ const dbPaths = stdout.trim().split('\n').filter(p => p);
268
+ if (dbPaths.length === 0) {
269
+ throw new Error('No database files found in backup');
270
+ }
271
+ // Find the common parent directory (backup root)
272
+ // Example: /tmp/backup-xxx/extracted/tmp/backup-name/admin -> parent is /tmp/backup-xxx/extracted/tmp/backup-name
273
+ const firstPath = dbPaths[0];
274
+ const pathParts = firstPath.split('/');
275
+ // The parent is everything except the last part (database name)
276
+ backupRootDir = pathParts.slice(0, -1).join('/');
277
+ // Get all database names from their directories
278
+ const dbNames = dbPaths
279
+ .map((p) => {
280
+ const parts = p.split('/');
281
+ return parts[parts.length - 1];
282
+ })
283
+ .filter((name, index, self) => self.indexOf(name) === index); // unique
284
+ // Filter out system databases
285
+ const userDatabases = dbNames.filter(name => !systemDatabases.includes(name));
286
+ if (userDatabases.length === 0) {
287
+ warning('Only system databases (admin, local, config) found in backup');
288
+ warning('Will proceed, but you may want to check the backup file');
289
+ sourceDbName = dbNames[0]; // Use first available database
290
+ }
291
+ else if (userDatabases.length === 1) {
292
+ sourceDbName = userDatabases[0];
293
+ findDbSpin.succeed(`Found database: ${sourceDbName}`);
294
+ }
295
+ else {
296
+ findDbSpin.succeed(`Found ${userDatabases.length} databases`);
297
+ // Let user select which database to restore
298
+ info('');
299
+ info('Multiple databases found in backup:');
300
+ userDatabases.forEach(db => info(` - ${db}`));
301
+ info('');
302
+ const { selectedDb } = yield prompt.ask({
303
+ choices: userDatabases,
304
+ initial: 0,
305
+ message: 'Select source database to restore:',
306
+ name: 'selectedDb',
307
+ type: 'select',
308
+ });
309
+ if (!selectedDb) {
310
+ error('No database selected');
311
+ // Cleanup
312
+ try {
313
+ yield fs.promises.rm(tempDir, { force: true, recursive: true });
314
+ }
315
+ catch (e) {
316
+ // Ignore cleanup errors
317
+ }
318
+ return;
319
+ }
320
+ sourceDbName = selectedDb;
321
+ success(`Selected source database: ${sourceDbName}`);
322
+ }
323
+ info(`Backup root directory: ${backupRootDir}`);
324
+ }
325
+ catch (err) {
326
+ error(`Could not detect databases: ${err.message}`);
327
+ // Cleanup
328
+ try {
329
+ yield fs.promises.rm(tempDir, { force: true, recursive: true });
330
+ }
331
+ catch (e) {
332
+ // Ignore cleanup errors
333
+ }
334
+ return;
335
+ }
336
+ // ============================================================================
337
+ // Step 7: MongoDB Configuration
338
+ // ============================================================================
339
+ info('');
340
+ info('Step 7: MongoDB Configuration');
341
+ info('');
342
+ const mongoUri = yield helper.getInput(parameters.options.mongoUri || process.env.MONGO_URI || 'mongodb://127.0.0.1:27017', {
343
+ initial: 'mongodb://127.0.0.1:27017',
344
+ name: 'MongoDB Connection URI (without database name)',
345
+ showError: true,
346
+ });
347
+ if (!mongoUri) {
348
+ error('MongoDB URI is required');
349
+ // Cleanup
350
+ try {
351
+ yield fs.promises.rm(tempDir, { force: true, recursive: true });
352
+ }
353
+ catch (e) {
354
+ // Ignore cleanup errors
355
+ }
356
+ return;
357
+ }
358
+ const targetDbName = yield helper.getInput(parameters.options.database || sourceDbName, {
359
+ initial: sourceDbName,
360
+ name: 'Target Database Name',
361
+ showError: true,
362
+ });
363
+ if (!targetDbName) {
364
+ error('Target database name is required');
365
+ // Cleanup
366
+ try {
367
+ yield fs.promises.rm(tempDir, { force: true, recursive: true });
368
+ }
369
+ catch (e) {
370
+ // Ignore cleanup errors
371
+ }
372
+ return;
373
+ }
374
+ // ============================================================================
375
+ // Step 8: Confirmation
376
+ // ============================================================================
377
+ info('');
378
+ warning('IMPORTANT: This operation will restore the backup to the target database.');
379
+ warning(`Target: ${mongoUri}/${targetDbName}`);
380
+ warning('If the database already exists, it may be overwritten or merged.');
381
+ info('');
382
+ const { confirmRestore } = yield prompt.ask({
383
+ initial: false,
384
+ message: `Proceed with restore to ${targetDbName}?`,
385
+ name: 'confirmRestore',
386
+ type: 'confirm',
387
+ });
388
+ if (!confirmRestore) {
389
+ info('Restore cancelled');
390
+ // Cleanup
391
+ try {
392
+ yield fs.promises.rm(tempDir, { force: true, recursive: true });
393
+ }
394
+ catch (e) {
395
+ // Ignore cleanup errors
396
+ }
397
+ return;
398
+ }
399
+ // ============================================================================
400
+ // Step 9: Restore Database
401
+ // ============================================================================
402
+ info('');
403
+ info('Step 9: Restoring database...');
404
+ info('');
405
+ try {
406
+ const restoreSpin = spin(`Restoring ${sourceDbName} to ${targetDbName}`);
407
+ // Build mongorestore command
408
+ // If source and target names are the same, use simpler command
409
+ let restoreCommand;
410
+ if (sourceDbName === targetDbName) {
411
+ // Restore without renaming
412
+ const fullMongoUri = `${mongoUri}/${targetDbName}`;
413
+ restoreCommand = `mongorestore --uri="${fullMongoUri}" --nsInclude="${sourceDbName}.*" "${backupRootDir}"`;
414
+ }
415
+ else {
416
+ // Restore with renaming using --nsFrom and --nsTo
417
+ restoreCommand = `mongorestore --uri="${mongoUri}" --nsFrom="${sourceDbName}.*" --nsTo="${targetDbName}.*" --nsInclude="${sourceDbName}.*" "${backupRootDir}"`;
418
+ }
419
+ info('Running: mongorestore ...');
420
+ const { stderr } = yield execAsync(restoreCommand, {
421
+ maxBuffer: 1024 * 1024 * 50, // 50MB buffer for large outputs
422
+ });
423
+ // Check for actual failures (mongorestore outputs warnings to stderr)
424
+ if (stderr && stderr.includes('Failed:') && !stderr.includes('0 document(s) failed')) {
425
+ throw new Error(stderr);
426
+ }
427
+ restoreSpin.succeed(`Database restored successfully (${sourceDbName} → ${targetDbName})`);
428
+ }
429
+ catch (err) {
430
+ error(`Failed to restore database: ${err.message}`);
431
+ info('');
432
+ info('Please ensure:');
433
+ info('- MongoDB is running and accessible');
434
+ info('- mongorestore tool is installed (part of MongoDB Database Tools)');
435
+ info('- The MongoDB URI is correct');
436
+ info('- The source database exists in the backup');
437
+ // Cleanup
438
+ try {
439
+ yield fs.promises.rm(tempDir, { force: true, recursive: true });
440
+ }
441
+ catch (e) {
442
+ // Ignore cleanup errors
443
+ }
444
+ return;
445
+ }
446
+ // ============================================================================
447
+ // Step 10: Cleanup
448
+ // ============================================================================
449
+ info('');
450
+ const cleanupSpin = spin('Cleaning up temporary files');
451
+ try {
452
+ yield fs.promises.rm(tempDir, { force: true, recursive: true });
453
+ cleanupSpin.succeed('Temporary files cleaned up');
454
+ }
455
+ catch (err) {
456
+ cleanupSpin.fail(`Failed to cleanup temporary files: ${err.message}`);
457
+ warning(`You may need to manually delete: ${tempDir}`);
458
+ }
459
+ // ============================================================================
460
+ // Done
461
+ // ============================================================================
462
+ info('');
463
+ success(`Database restored successfully to ${targetDbName} in ${helper.msToMinutesAndSeconds(timer())}m`);
464
+ info('');
465
+ if (!parameters.options.fromGluegunMenu) {
466
+ process.exit(0);
467
+ }
468
+ return `mongodb restored ${targetDbName}`;
469
+ }),
470
+ };
471
+ exports.default = command;
472
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiczMtcmVzdG9yZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9jb21tYW5kcy9tb25nb2RiL3MzLXJlc3RvcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUEsa0RBQStEO0FBQy9ELHlCQUF5QjtBQUV6Qiw2QkFBNkI7QUFDN0IsK0JBQWlDO0FBSWpDLE1BQU0sU0FBUyxHQUFHLElBQUEsZ0JBQVMsRUFBQyxPQUFPLENBQUMsZUFBZSxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUM7QUFTM0Q7O0dBRUc7QUFDSCxNQUFNLE9BQU8sR0FBbUI7SUFDOUIsS0FBSyxFQUFFLENBQUMsS0FBSyxDQUFDO0lBQ2QsV0FBVyxFQUFFLHlDQUF5QztJQUN0RCxJQUFJLEVBQUUsWUFBWTtJQUNsQixHQUFHLEVBQUUsQ0FBTyxPQUErQixFQUFFLEVBQUU7O1FBQzdDLE1BQU0sRUFDSixNQUFNLEVBQ04sVUFBVSxFQUNWLEtBQUssRUFBRSxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLE9BQU8sRUFBRSxPQUFPLEVBQUUsRUFDOUMsTUFBTSxFQUNOLE1BQU0sR0FDUCxHQUFHLE9BQU8sQ0FBQztRQUVaLGNBQWM7UUFDZCxNQUFNLEtBQUssR0FBRyxNQUFNLENBQUMsVUFBVSxFQUFFLENBQUM7UUFFbEMsSUFBSSxDQUFDLHlCQUF5QixDQUFDLENBQUM7UUFDaEMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBRVQsK0VBQStFO1FBQy9FLDJCQUEyQjtRQUMzQiwrRUFBK0U7UUFFL0UsSUFBSSxDQUFDLDBCQUEwQixDQUFDLENBQUM7UUFDakMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBRVQsTUFBTSxRQUFRLEdBQUcsTUFBTSxNQUFNLENBQUMsUUFBUSxDQUFDLFVBQVUsQ0FBQyxPQUFPLENBQUMsTUFBTSxJQUFJLE9BQU8sQ0FBQyxHQUFHLENBQUMsU0FBUyxFQUFFO1lBQ3pGLElB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