dbdock 1.1.1 → 1.1.2

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/README.md CHANGED
@@ -24,7 +24,7 @@ npx dbdock restore # Restore backup
24
24
  - **Security** - AES-256 encryption, Brotli compression
25
25
  - **Retention Policies** - Automatic cleanup by count/age with safety nets
26
26
  - **Smart UX** - Intelligent filtering for 100+ backups, clear error messages
27
- - **Email Alerts** - SMTP notifications with custom templates
27
+ - **Alerts** - Email (SMTP) and Slack notifications for backups (CLI & Programmatic)
28
28
  - **TypeScript Native** - Full type safety for programmatic usage
29
29
  - **Automation** - Cron schedules, auto-cleanup after backups
30
30
 
@@ -57,7 +57,7 @@ Interactive setup wizard that creates `dbdock.config.json` with:
57
57
  - Database connection (host, port, credentials)
58
58
  - Storage provider (Local, S3, R2, Cloudinary)
59
59
  - Encryption/compression settings
60
- - Email alerts (optional)
60
+ - Email and Slack alerts (optional)
61
61
 
62
62
  Auto-adds config to `.gitignore` to protect credentials.
63
63
 
@@ -115,6 +115,15 @@ Progress:
115
115
  - Date range (24h, 7d, 30d, 90d, custom)
116
116
  - Search by keyword/ID
117
117
 
118
+ **Migration Support:**
119
+
120
+ You can choose to restore to a **New Database Instance** during the restore process. This is perfect for migrating data between servers (e.g., from staging to production or local to cloud).
121
+
122
+ 1. Run `npx dbdock restore`
123
+ 2. Select a backup
124
+ 3. Choose "New Database Instance (Migrate)"
125
+ 4. Enter connection details for the target database
126
+
118
127
  Shows database stats and requires confirmation before restore.
119
128
 
120
129
  ### `npx dbdock list`
@@ -534,12 +543,12 @@ startScheduler().catch(console.error);
534
543
 
535
544
  **Note:** The CLI `dbdock schedule` command manages configuration for external schedulers but does not run a daemon itself. Using `node-cron` as shown above is the recommended way to run scheduled backups programmatically.
536
545
 
537
- ### Email Alerts
538
-
539
- DBDock can send email notifications when backups complete (success or failure). Email alerts work **only with programmatic usage** - they don't send for CLI commands like `npx dbdock backup`.
540
-
546
+ ### Alerts
547
+
548
+ DBDock can send notifications when backups complete (success or failure) via Email and Slack. Alerts work with both **programmatic usage** and **CLI commands**.
549
+
541
550
  **Configuration in `dbdock.config.json`:**
542
-
551
+
543
552
  ```json
544
553
  {
545
554
  "database": { ... },
@@ -559,13 +568,24 @@ DBDock can send email notifications when backups complete (success or failure).
559
568
  },
560
569
  "from": "backups@yourapp.com",
561
570
  "to": ["admin@yourapp.com", "devops@yourapp.com"]
571
+ },
572
+ "slack": {
573
+ "enabled": true,
574
+ "webhookUrl": "https://hooks.slack.com/services/..."
562
575
  }
563
576
  }
564
577
  }
565
578
  ```
566
-
579
+
580
+ **Slack Configuration:**
581
+
582
+ 1. Create a Slack App or use an existing one.
583
+ 2. Enable "Incoming Webhooks".
584
+ 3. Create a new Webhook URL for your channel.
585
+ 4. Run `npx dbdock init` and paste the URL when prompted.
586
+
567
587
  **SMTP Provider Examples:**
568
-
588
+
569
589
  _Gmail:_
570
590
  ```json
571
591
  {
@@ -580,9 +600,9 @@ _Gmail:_
580
600
  }
581
601
  }
582
602
  ```
583
-
603
+
584
604
  > **Note:** For Gmail, you need to [create an App Password](https://support.google.com/accounts/answer/185833) instead of using your regular password.
585
-
605
+
586
606
  _SendGrid:_
587
607
  ```json
588
608
  {
@@ -597,7 +617,7 @@ _SendGrid:_
597
617
  }
598
618
  }
599
619
  ```
600
-
620
+
601
621
  _AWS SES:_
602
622
  ```json
603
623
  {
@@ -612,7 +632,7 @@ _AWS SES:_
612
632
  }
613
633
  }
614
634
  ```
615
-
635
+
616
636
  _Mailgun:_
617
637
  ```json
618
638
  {
@@ -627,61 +647,59 @@ _Mailgun:_
627
647
  }
628
648
  }
629
649
  ```
630
-
631
- **Using Email Alerts Programmatically:**
632
-
633
- Once configured in `dbdock.config.json`, email alerts are sent automatically when you create backups programmatically:
634
-
650
+
651
+ **Using Alerts Programmatically:**
652
+
653
+ Once configured in `dbdock.config.json`, alerts are sent automatically when you create backups programmatically:
654
+
635
655
  ```javascript
636
656
  const { createDBDock, BackupService } = require('dbdock');
637
-
638
- async function createBackupWithEmail() {
657
+
658
+ async function createBackupWithAlerts() {
639
659
  const dbdock = await createDBDock();
640
660
  const backupService = dbdock.get(BackupService);
641
-
642
- // Email will be sent automatically after backup completes
661
+
662
+ // Alerts will be sent automatically after backup completes
643
663
  const result = await backupService.createBackup({
644
664
  compress: true,
645
665
  encrypt: true,
646
666
  });
647
-
667
+
648
668
  console.log(`Backup created: ${result.metadata.id}`);
649
- // Email sent to addresses in config
669
+ // Alerts sent to configured channels
650
670
  }
651
-
652
- createBackupWithEmail().catch(console.error);
671
+
672
+ createBackupWithAlerts().catch(console.error);
653
673
  ```
654
-
655
- **Email Content:**
656
-
657
- Success emails include:
674
+
675
+ **Alert Content:**
676
+
677
+ Success alerts include:
658
678
  - Backup ID
659
679
  - Database name
660
680
  - Size (original and compressed)
661
681
  - Duration
662
682
  - Storage location
663
683
  - Encryption status
664
-
665
- Failure emails include:
684
+
685
+ Failure alerts include:
666
686
  - Error message
667
687
  - Database details
668
688
  - Timestamp
669
689
  - Helpful troubleshooting tips
670
-
671
- **Testing Email Configuration:**
672
-
673
- Run `npx dbdock test` to validate your SMTP settings without creating a backup.
674
-
690
+
691
+ **Testing Alert Configuration:**
692
+
693
+ Run `npx dbdock test` to validate your configuration without creating a backup.
694
+
675
695
  **Important Notes:**
676
-
677
- - ✅ Emails work with programmatic usage (`createBackup()`)
678
- - ✅ Emails work with scheduled backups (cron jobs in your app)
679
- - Emails do NOT work with CLI commands (`npx dbdock backup`)
680
- - Email configuration is read from `dbdock.config.json` automatically
681
- - Multiple recipients supported in the `to` array
682
- - Emails are sent asynchronously (won't block backup completion)
683
-
684
-
696
+
697
+ - ✅ Alerts work with programmatic usage (`createBackup()`)
698
+ - ✅ Alerts work with scheduled backups (cron jobs in your app)
699
+ - Alerts work with CLI commands (`npx dbdock backup`)
700
+ - Configuration is read from `dbdock.config.json` automatically
701
+ - Multiple recipients supported in the `to` array for email
702
+ - Alerts are sent asynchronously (won't block backup completion)
685
703
 
686
704
  ## Requirements
687
705
 
@@ -18,6 +18,7 @@ export declare class AlertService {
18
18
  }): Promise<void>;
19
19
  sendStorageErrorAlert(error: Error): Promise<void>;
20
20
  private sendAlert;
21
+ private sendSlackAlert;
21
22
  private sendEmail;
22
23
  verifyConnection(): Promise<boolean>;
23
24
  }
@@ -41,6 +41,9 @@ var __importStar = (this && this.__importStar) || (function () {
41
41
  var __metadata = (this && this.__metadata) || function (k, v) {
42
42
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
43
43
  };
44
+ var __importDefault = (this && this.__importDefault) || function (mod) {
45
+ return (mod && mod.__esModule) ? mod : { "default": mod };
46
+ };
44
47
  var AlertService_1;
45
48
  Object.defineProperty(exports, "__esModule", { value: true });
46
49
  exports.AlertService = void 0;
@@ -49,6 +52,7 @@ const nodemailer = __importStar(require("nodemailer"));
49
52
  const config_service_1 = require("../config/config.service");
50
53
  const alert_types_1 = require("./alert.types");
51
54
  const alert_templates_1 = require("./alert-templates");
55
+ const node_fetch_1 = __importDefault(require("node-fetch"));
52
56
  let AlertService = AlertService_1 = class AlertService {
53
57
  configService;
54
58
  logger = new common_1.Logger(AlertService_1.name);
@@ -61,23 +65,28 @@ let AlertService = AlertService_1 = class AlertService {
61
65
  initializeTransporter() {
62
66
  const alertsConfig = this.configService.get('alerts');
63
67
  if (!alertsConfig) {
64
- this.logger.log('Email alerts disabled - no alerts configuration found');
68
+ this.logger.log('Alerts disabled - no alerts configuration found');
65
69
  return;
66
70
  }
67
- try {
68
- this.transporter = nodemailer.createTransport({
69
- host: alertsConfig.smtpHost,
70
- port: alertsConfig.smtpPort,
71
- secure: alertsConfig.smtpPort === 465,
72
- auth: {
73
- user: alertsConfig.smtpUser,
74
- pass: alertsConfig.smtpPass,
75
- },
76
- });
77
- this.logger.log(`Email alerts enabled - configured for ${alertsConfig.smtpHost}:${alertsConfig.smtpPort}`);
71
+ if (alertsConfig.smtpHost) {
72
+ try {
73
+ this.transporter = nodemailer.createTransport({
74
+ host: alertsConfig.smtpHost,
75
+ port: alertsConfig.smtpPort,
76
+ secure: alertsConfig.smtpPort === 465,
77
+ auth: {
78
+ user: alertsConfig.smtpUser,
79
+ pass: alertsConfig.smtpPass,
80
+ },
81
+ });
82
+ this.logger.log(`Email alerts enabled - configured for ${alertsConfig.smtpHost}:${alertsConfig.smtpPort}`);
83
+ }
84
+ catch (error) {
85
+ this.logger.error(`Failed to initialize email transporter: ${error.message}`);
86
+ }
78
87
  }
79
- catch (error) {
80
- this.logger.error(`Failed to initialize email transporter: ${error.message}`);
88
+ if (alertsConfig.slackWebhook) {
89
+ this.logger.log('Slack alerts enabled');
81
90
  }
82
91
  }
83
92
  setCustomTemplate(type, template) {
@@ -85,8 +94,6 @@ let AlertService = AlertService_1 = class AlertService {
85
94
  this.logger.log(`Custom template set for alert type: ${type}`);
86
95
  }
87
96
  async sendBackupSuccessAlert(metadata, downloadUrl) {
88
- if (!this.transporter)
89
- return;
90
97
  const context = {
91
98
  database: metadata.database,
92
99
  backupId: metadata.id,
@@ -104,8 +111,6 @@ let AlertService = AlertService_1 = class AlertService {
104
111
  });
105
112
  }
106
113
  async sendBackupFailureAlert(metadata, error) {
107
- if (!this.transporter)
108
- return;
109
114
  const context = {
110
115
  database: metadata.database,
111
116
  backupId: metadata.id,
@@ -120,8 +125,6 @@ let AlertService = AlertService_1 = class AlertService {
120
125
  });
121
126
  }
122
127
  async sendRetentionCleanupAlert(details) {
123
- if (!this.transporter)
124
- return;
125
128
  const context = {
126
129
  backupsDeleted: details.backupsDeleted,
127
130
  walFilesDeleted: details.walFilesDeleted,
@@ -134,8 +137,6 @@ let AlertService = AlertService_1 = class AlertService {
134
137
  });
135
138
  }
136
139
  async sendStorageErrorAlert(error) {
137
- if (!this.transporter)
138
- return;
139
140
  const context = {
140
141
  error: error.message,
141
142
  timestamp: new Date().toLocaleString(),
@@ -147,27 +148,88 @@ let AlertService = AlertService_1 = class AlertService {
147
148
  });
148
149
  }
149
150
  async sendAlert(alertContext) {
150
- if (!this.transporter)
151
- return;
152
151
  const alertsConfig = this.configService.get('alerts');
153
152
  if (!alertsConfig)
154
153
  return;
155
- try {
156
- const template = this.customTemplates[alertContext.type] ||
157
- alert_templates_1.DEFAULT_TEMPLATES[alertContext.type];
158
- const subject = (0, alert_templates_1.renderTemplate)(template.subject, alertContext.details || {});
159
- const html = (0, alert_templates_1.renderTemplate)(template.body, alertContext.details || {});
160
- const text = html.replace(/<[^>]*>/g, '');
161
- await this.sendEmail({
162
- to: alertsConfig.to,
163
- subject,
164
- html,
165
- text,
166
- });
167
- this.logger.log(`Alert sent: ${alertContext.type} to ${alertsConfig.to.join(', ')}`);
154
+ if (this.transporter && alertsConfig.to) {
155
+ try {
156
+ const template = this.customTemplates[alertContext.type] ||
157
+ alert_templates_1.DEFAULT_TEMPLATES[alertContext.type];
158
+ const subject = (0, alert_templates_1.renderTemplate)(template.subject, alertContext.details || {});
159
+ const html = (0, alert_templates_1.renderTemplate)(template.body, alertContext.details || {});
160
+ const text = html.replace(/<[^>]*>/g, '');
161
+ await this.sendEmail({
162
+ to: alertsConfig.to,
163
+ subject,
164
+ html,
165
+ text,
166
+ });
167
+ this.logger.log(`Email alert sent: ${alertContext.type} to ${alertsConfig.to.join(', ')}`);
168
+ }
169
+ catch (error) {
170
+ this.logger.error(`Failed to send email alert (${alertContext.type}): ${error.message}`);
171
+ }
168
172
  }
169
- catch (error) {
170
- this.logger.error(`Failed to send alert (${alertContext.type}): ${error.message}`);
173
+ if (alertsConfig.slackWebhook) {
174
+ try {
175
+ await this.sendSlackAlert(alertsConfig.slackWebhook, alertContext);
176
+ this.logger.log(`Slack alert sent: ${alertContext.type}`);
177
+ }
178
+ catch (error) {
179
+ this.logger.error(`Failed to send Slack alert (${alertContext.type}): ${error.message}`);
180
+ }
181
+ }
182
+ }
183
+ async sendSlackAlert(webhookUrl, context) {
184
+ let color = '#36a64f';
185
+ let title = 'DBDock Alert';
186
+ let text = '';
187
+ switch (context.type) {
188
+ case alert_types_1.AlertType.BACKUP_SUCCESS:
189
+ title = '✅ Backup Successful';
190
+ text = `Database: *${context.details?.database}*\nSize: ${context.details?.size} MB\nDuration: ${context.details?.duration}s`;
191
+ break;
192
+ case alert_types_1.AlertType.BACKUP_FAILURE:
193
+ color = '#dc3545';
194
+ title = '❌ Backup Failed';
195
+ text = `Database: *${context.details?.database}*\nError: ${context.details?.error}`;
196
+ break;
197
+ case alert_types_1.AlertType.RETENTION_CLEANUP:
198
+ color = '#17a2b8';
199
+ title = '🧹 Retention Cleanup';
200
+ text = `Deleted ${context.details?.backupsDeleted} backups\nFreed ${context.details?.spaceFreed} MB`;
201
+ break;
202
+ case alert_types_1.AlertType.STORAGE_ERROR:
203
+ color = '#ffc107';
204
+ title = '⚠️ Storage Error';
205
+ text = `Error: ${context.details?.error}`;
206
+ break;
207
+ }
208
+ const payload = {
209
+ attachments: [
210
+ {
211
+ color,
212
+ title,
213
+ text,
214
+ fields: Object.entries(context.details || {})
215
+ .filter(([key]) => !['database', 'size', 'duration', 'error', 'backupsDeleted', 'spaceFreed'].includes(key))
216
+ .map(([key, value]) => ({
217
+ title: key.charAt(0).toUpperCase() + key.slice(1),
218
+ value: String(value),
219
+ short: true,
220
+ })),
221
+ footer: 'DBDock',
222
+ ts: Math.floor(Date.now() / 1000),
223
+ },
224
+ ],
225
+ };
226
+ const response = await (0, node_fetch_1.default)(webhookUrl, {
227
+ method: 'POST',
228
+ headers: { 'Content-Type': 'application/json' },
229
+ body: JSON.stringify(payload),
230
+ });
231
+ if (!response.ok) {
232
+ throw new Error(`Slack API error: ${response.statusText}`);
171
233
  }
172
234
  }
173
235
  async sendEmail(options) {
@@ -1 +1 @@
1
- {"version":3,"file":"alert.service.js","sourceRoot":"","sources":["../../src/alerts/alert.service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"alert.service.js","sourceRoot":"","sources":["../../src/alerts/alert.service.ts"],"names":[],"mappings":"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"}
@@ -10,10 +10,8 @@ const logger_1 = require("../utils/logger");
10
10
  const backup_standalone_1 = require("../../standalone/backup-standalone");
11
11
  const common_1 = require("@nestjs/common");
12
12
  const progress_1 = require("../utils/progress");
13
- const retention_1 = require("../utils/retention");
14
- const local_adapter_1 = require("../../storage/adapters/local.adapter");
15
- const s3_adapter_1 = require("../../storage/adapters/s3.adapter");
16
- const cloudinary_adapter_1 = require("../../storage/adapters/cloudinary.adapter");
13
+ const alert_service_1 = require("../../alerts/alert.service");
14
+ const backup_types_1 = require("../../backup/backup.types");
17
15
  common_1.Logger.overrideLogger(false);
18
16
  async function backupCommand(options) {
19
17
  const spinner = (0, ora_1.default)('Loading configuration...').start();
@@ -33,40 +31,15 @@ async function backupCommand(options) {
33
31
  },
34
32
  },
35
33
  };
36
- if (mergedConfig.backup?.encryption?.enabled && !mergedConfig.backup.encryption.key) {
37
- spinner.fail('Encryption enabled but no key provided');
38
- logger_1.logger.error('\nPlease provide an encryption key:\n');
39
- logger_1.logger.log(' Option 1: Add to config file (dbdock.config.json):');
40
- logger_1.logger.log(' "backup": { "encryption": { "key": "YOUR_64_CHAR_HEX_KEY" } }\n');
41
- logger_1.logger.log(' Option 2: Use CLI flag:');
42
- logger_1.logger.log(' npx dbdock backup --encryption-key YOUR_64_CHAR_HEX_KEY\n');
43
- logger_1.logger.log(' Generate a key:');
44
- logger_1.logger.log(' node -e "console.log(require(\'crypto\').randomBytes(32).toString(\'hex\'))"');
45
- process.exit(1);
46
- }
47
- if (mergedConfig.backup?.encryption?.key) {
48
- const key = mergedConfig.backup.encryption.key;
49
- if (key.length !== 64) {
50
- spinner.fail('Invalid encryption key length');
51
- logger_1.logger.error(`\nYour key has ${key.length} characters, but must be exactly 64 hexadecimal characters (32 bytes)\n`);
52
- logger_1.logger.log('Please fix:\n');
53
- logger_1.logger.log(' Generate a valid key:');
54
- logger_1.logger.log(' node -e "console.log(require(\'crypto\').randomBytes(32).toString(\'hex\'))"\n');
55
- logger_1.logger.log(' Update your dbdock.config.json:');
56
- logger_1.logger.log(' "backup": { "encryption": { "key": "PASTE_64_CHAR_KEY_HERE" } }');
57
- process.exit(1);
58
- }
59
- if (!/^[0-9a-fA-F]{64}$/.test(key)) {
60
- spinner.fail('Invalid encryption key format');
61
- logger_1.logger.error('\nEncryption key must contain only hexadecimal characters (0-9, a-f, A-F)\n');
62
- logger_1.logger.log('Please fix:\n');
63
- logger_1.logger.log(' Generate a valid key:');
64
- logger_1.logger.log(' node -e "console.log(require(\'crypto\').randomBytes(32).toString(\'hex\'))"\n');
65
- logger_1.logger.log(' Update your dbdock.config.json:');
66
- logger_1.logger.log(' "backup": { "encryption": { "key": "PASTE_64_CHAR_KEY_HERE" } }');
67
- process.exit(1);
68
- }
69
- }
34
+ const mockConfigService = {
35
+ get: (key) => {
36
+ if (key === 'alerts') {
37
+ return mergedConfig.alerts;
38
+ }
39
+ return null;
40
+ },
41
+ };
42
+ const alertService = new alert_service_1.AlertService(mockConfigService);
70
43
  spinner.succeed('Configuration validated');
71
44
  const progress = new progress_1.ProgressTracker();
72
45
  let currentStage = 'Starting';
@@ -83,115 +56,51 @@ async function backupCommand(options) {
83
56
  progress.stop('Backup complete');
84
57
  console.log('');
85
58
  logger_1.logger.success('Backup completed successfully');
59
+ await alertService.sendBackupSuccessAlert({
60
+ id: result.backupId,
61
+ database: mergedConfig.database.database || 'unknown',
62
+ size: result.size,
63
+ compressedSize: result.size,
64
+ duration: result.duration,
65
+ endTime: new Date(),
66
+ startTime: new Date(Date.now() - result.duration),
67
+ storageKey: result.storageKey,
68
+ type: backup_types_1.BackupType.FULL,
69
+ status: backup_types_1.BackupStatus.COMPLETED,
70
+ compression: { enabled: mergedConfig.backup?.compression?.enabled || false },
71
+ }, result.downloadUrl);
86
72
  logger_1.logger.success(`Backup ID: ${result.backupId}`);
87
- logger_1.logger.info(`Size: ${(result.size / 1024 / 1024).toFixed(2)} MB`);
88
- logger_1.logger.info(`Duration: ${result.duration}ms`);
89
- if (mergedConfig.backup?.encryption?.enabled) {
90
- logger_1.logger.info('Encryption: enabled');
91
- }
92
- if (mergedConfig.backup?.compression?.enabled) {
93
- logger_1.logger.info(`Compression: enabled (level ${mergedConfig.backup.compression.level || 6})`);
94
- }
95
- logger_1.logger.info(`\nStorage Location:`);
96
- if (mergedConfig.storage.provider === 'local') {
97
- logger_1.logger.log(` Local path: ${result.storageKey}`);
98
- }
99
- else if (mergedConfig.storage.provider === 's3') {
100
- const s3Config = mergedConfig.storage.s3;
101
- const region = s3Config?.region || 'us-east-1';
102
- const bucket = s3Config?.bucket || '';
103
- logger_1.logger.log(` Provider: AWS S3`);
104
- logger_1.logger.log(` Bucket: ${bucket}`);
105
- logger_1.logger.log(` Region: ${region}`);
106
- logger_1.logger.log(` Key: ${result.storageKey}`);
107
- if (result.downloadUrl) {
108
- logger_1.logger.log(` Download URL: ${result.downloadUrl}`);
109
- }
110
- logger_1.logger.log(` Console: https://s3.console.aws.amazon.com/s3/object/${bucket}?region=${region}&prefix=${result.storageKey}`);
111
- }
112
- else if (mergedConfig.storage.provider === 'r2') {
113
- const s3Config = mergedConfig.storage.s3;
114
- const bucket = s3Config?.bucket || '';
115
- const accountId = s3Config?.endpoint?.match(/https:\/\/([^.]+)/)?.[1] || '';
116
- logger_1.logger.log(` Provider: Cloudflare R2`);
117
- logger_1.logger.log(` Bucket: ${bucket}`);
118
- logger_1.logger.log(` Key: ${result.storageKey}`);
119
- if (result.downloadUrl) {
120
- logger_1.logger.log(` Download URL: ${result.downloadUrl}`);
121
- }
122
- if (accountId) {
123
- logger_1.logger.log(` Dashboard: https://dash.cloudflare.com/${accountId}/r2/default/buckets/${bucket}`);
124
- }
125
- }
126
- else if (mergedConfig.storage.provider === 'cloudinary') {
127
- const cloudinaryConfig = mergedConfig.storage.cloudinary;
128
- const cloudName = cloudinaryConfig?.cloudName || '';
129
- logger_1.logger.log(` Provider: Cloudinary`);
130
- logger_1.logger.log(` Cloud: ${cloudName}`);
131
- logger_1.logger.log(` Resource ID: ${result.storageKey}`);
132
- if (result.downloadUrl) {
133
- logger_1.logger.log(` Download URL: ${result.downloadUrl}`);
134
- }
135
- logger_1.logger.log(` Console: https://console.cloudinary.com/console/${cloudName}/media_library`);
136
- }
137
- if (mergedConfig.backup?.retention?.enabled && mergedConfig.backup.retention.runAfterBackup) {
138
- logger_1.logger.info('\nRunning retention policy...');
139
- let adapter;
140
- switch (mergedConfig.storage.provider) {
141
- case 'local':
142
- adapter = new local_adapter_1.LocalStorageAdapter(mergedConfig.storage.local?.path || './backups');
143
- break;
144
- case 's3':
145
- case 'r2':
146
- if (!mergedConfig.storage.s3?.accessKeyId || !mergedConfig.storage.s3?.secretAccessKey) {
147
- logger_1.logger.warn('Skipping retention: Storage credentials missing');
148
- return;
149
- }
150
- adapter = new s3_adapter_1.S3StorageAdapter({
151
- endpoint: mergedConfig.storage.s3.endpoint,
152
- bucket: mergedConfig.storage.s3.bucket || '',
153
- region: mergedConfig.storage.s3.region,
154
- accessKeyId: mergedConfig.storage.s3.accessKeyId,
155
- secretAccessKey: mergedConfig.storage.s3.secretAccessKey,
156
- });
157
- break;
158
- case 'cloudinary':
159
- if (!mergedConfig.storage.cloudinary?.cloudName || !mergedConfig.storage.cloudinary?.apiKey || !mergedConfig.storage.cloudinary?.apiSecret) {
160
- logger_1.logger.warn('Skipping retention: Storage credentials missing');
161
- return;
162
- }
163
- adapter = new cloudinary_adapter_1.CloudinaryStorageAdapter({
164
- cloudName: mergedConfig.storage.cloudinary.cloudName,
165
- apiKey: mergedConfig.storage.cloudinary.apiKey,
166
- apiSecret: mergedConfig.storage.cloudinary.apiSecret,
167
- folder: 'dbdock_backups',
168
- });
169
- break;
170
- default:
171
- logger_1.logger.warn(`Skipping retention: Unknown storage provider ${mergedConfig.storage.provider}`);
172
- return;
173
- }
174
- try {
175
- const retentionStats = await (0, retention_1.applyRetention)(adapter, mergedConfig.backup.retention, mergedConfig.storage.provider, false);
176
- if (retentionStats.deletedBackups > 0) {
177
- logger_1.logger.success(`\nRetention cleanup completed:`);
178
- logger_1.logger.log((0, retention_1.formatRetentionStats)(retentionStats));
179
- }
180
- else {
181
- logger_1.logger.info('No backups need to be deleted');
182
- }
183
- if (retentionStats.errors.length > 0) {
184
- logger_1.logger.warn(`\nRetention encountered ${retentionStats.errors.length} error(s)`);
185
- }
186
- }
187
- catch (err) {
188
- logger_1.logger.warn(`Retention policy failed: ${err instanceof Error ? err.message : String(err)}`);
189
- }
190
- }
191
73
  }
192
74
  catch (error) {
193
75
  spinner.fail('Backup failed');
194
76
  logger_1.logger.error(error instanceof Error ? error.message : String(error));
77
+ try {
78
+ const config = (0, config_1.loadConfig)();
79
+ const mockConfigService = {
80
+ get: (key) => {
81
+ if (key === 'alerts') {
82
+ return config.alerts;
83
+ }
84
+ return null;
85
+ },
86
+ };
87
+ const alertService = new alert_service_1.AlertService(mockConfigService);
88
+ await alertService.sendBackupFailureAlert({
89
+ id: 'failed-backup',
90
+ database: config.database?.database || 'unknown',
91
+ size: 0,
92
+ compressedSize: 0,
93
+ duration: 0,
94
+ endTime: new Date(),
95
+ startTime: new Date(),
96
+ storageKey: '',
97
+ type: backup_types_1.BackupType.FULL,
98
+ status: backup_types_1.BackupStatus.FAILED,
99
+ compression: { enabled: false },
100
+ }, error instanceof Error ? error : new Error(String(error)));
101
+ }
102
+ catch (alertError) {
103
+ }
195
104
  process.exit(1);
196
105
  }
197
106
  }