dbdock 1.1.0 → 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 +307 -95
- package/dist/alerts/alert.service.d.ts +1 -0
- package/dist/alerts/alert.service.js +101 -39
- package/dist/alerts/alert.service.js.map +1 -1
- package/dist/backup/backup.service.js +28 -7
- package/dist/backup/backup.service.js.map +1 -1
- package/dist/backup/backup.types.d.ts +2 -0
- package/dist/cli/commands/backup.js +51 -142
- package/dist/cli/commands/backup.js.map +1 -1
- package/dist/cli/commands/init.js +51 -24
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/restore.js +70 -9
- package/dist/cli/commands/restore.js.map +1 -1
- package/dist/cli/utils/config.d.ts +4 -0
- package/dist/cli/utils/config.js.map +1 -1
- package/dist/config/config.schema.d.ts +6 -5
- package/dist/config/config.schema.js +11 -0
- package/dist/config/config.schema.js.map +1 -1
- package/dist/config/config.service.d.ts +2 -0
- package/dist/config/config.service.js +175 -12
- package/dist/config/config.service.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/standalone/backup-standalone.d.ts +1 -0
- package/dist/standalone/backup-standalone.js +2 -0
- package/dist/standalone/backup-standalone.js.map +1 -1
- package/dist/storage/adapters/local.adapter.js +1 -1
- package/dist/storage/adapters/local.adapter.js.map +1 -1
- package/dist/utils/format.d.ts +1 -0
- package/dist/utils/format.js +13 -0
- package/dist/utils/format.js.map +1 -0
- package/package.json +1 -1
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
|
-
- **
|
|
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
|
|
|
@@ -33,7 +33,7 @@ npx dbdock restore # Restore backup
|
|
|
33
33
|
**Global Installation (Recommended):**
|
|
34
34
|
|
|
35
35
|
```bash
|
|
36
|
-
npm install -g dbdock
|
|
36
|
+
npm install -g dbdock
|
|
37
37
|
|
|
38
38
|
dbdock init # Use directly
|
|
39
39
|
dbdock backup
|
|
@@ -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`
|
|
@@ -210,7 +219,7 @@ dbdock schedule
|
|
|
210
219
|
- Every month (1st): `0 0 1 * *`
|
|
211
220
|
- Custom cron expression
|
|
212
221
|
|
|
213
|
-
**⚠️ Important:** Schedules only execute when DBDock is integrated into your
|
|
222
|
+
**⚠️ Important:** Schedules only execute when DBDock is integrated into your Node.js application (see Programmatic Usage below). The CLI is for configuration only.
|
|
214
223
|
|
|
215
224
|
## Configuration
|
|
216
225
|
|
|
@@ -367,127 +376,330 @@ Automatic cleanup to prevent storage bloat from frequent backups:
|
|
|
367
376
|
|
|
368
377
|
## Programmatic Usage
|
|
369
378
|
|
|
370
|
-
|
|
379
|
+
Use DBDock in your Node.js application to create backups programmatically. You don't need to understand NestJS internals - DBDock provides a simple API that works with any Node.js backend.
|
|
371
380
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
DBDockModule.forRoot({
|
|
379
|
-
database: {
|
|
380
|
-
type: 'postgres',
|
|
381
|
-
host: 'localhost',
|
|
382
|
-
port: 5432,
|
|
383
|
-
username: 'postgres',
|
|
384
|
-
password: process.env.DB_PASSWORD,
|
|
385
|
-
database: 'myapp',
|
|
386
|
-
},
|
|
387
|
-
storage: {
|
|
388
|
-
provider: 's3',
|
|
389
|
-
s3: {
|
|
390
|
-
bucket: 'my-backups',
|
|
391
|
-
region: 'us-east-1',
|
|
392
|
-
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
|
393
|
-
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
|
394
|
-
},
|
|
395
|
-
},
|
|
396
|
-
backup: {
|
|
397
|
-
compression: { enabled: true, level: 6 },
|
|
398
|
-
encryption: { enabled: true, key: process.env.ENCRYPTION_KEY },
|
|
399
|
-
schedules: [
|
|
400
|
-
{ name: 'Daily Backup', cron: '0 2 * * *', enabled: true },
|
|
401
|
-
{ name: 'Weekly Full', cron: '0 0 * * 0', enabled: true },
|
|
402
|
-
],
|
|
403
|
-
},
|
|
404
|
-
}),
|
|
405
|
-
],
|
|
406
|
-
})
|
|
407
|
-
export class AppModule {}
|
|
381
|
+
### Basic Setup
|
|
382
|
+
|
|
383
|
+
First, install DBDock:
|
|
384
|
+
|
|
385
|
+
```bash
|
|
386
|
+
npm install dbdock
|
|
408
387
|
```
|
|
409
388
|
|
|
410
|
-
|
|
389
|
+
Make sure you have `dbdock.config.json` configured (run `npx dbdock init` first). DBDock reads all configuration from this file automatically.
|
|
411
390
|
|
|
412
|
-
|
|
413
|
-
import { BackupService } from 'dbdock';
|
|
391
|
+
### How It Works
|
|
414
392
|
|
|
415
|
-
|
|
416
|
-
export class MyService {
|
|
417
|
-
constructor(private backupService: BackupService) {}
|
|
393
|
+
DBDock uses a simple initialization pattern:
|
|
418
394
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
encrypt: true,
|
|
423
|
-
});
|
|
395
|
+
1. Call `createDBDock()` to initialize DBDock (reads from `dbdock.config.json`)
|
|
396
|
+
2. Get the `BackupService` from the returned context using `.get(BackupService)`
|
|
397
|
+
3. Use the service methods to create backups, list backups, etc.
|
|
424
398
|
|
|
425
|
-
|
|
426
|
-
return result;
|
|
427
|
-
}
|
|
399
|
+
Think of `createDBDock()` as a factory function that sets up everything for you based on your config file.
|
|
428
400
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
401
|
+
### Creating Backups
|
|
402
|
+
|
|
403
|
+
```javascript
|
|
404
|
+
const { createDBDock, BackupService } = require('dbdock');
|
|
405
|
+
|
|
406
|
+
async function createBackup() {
|
|
407
|
+
const dbdock = await createDBDock();
|
|
408
|
+
const backupService = dbdock.get(BackupService);
|
|
409
|
+
|
|
410
|
+
const result = await backupService.createBackup({
|
|
411
|
+
format: 'plain', // 'custom' (binary), 'plain' (sql), 'directory', 'tar'
|
|
412
|
+
compress: true,
|
|
413
|
+
encrypt: true,
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
console.log(`Backup created: ${result.metadata.id}`);
|
|
417
|
+
console.log(`Size: ${result.metadata.formattedSize}`); // e.g. "108.3 KB"
|
|
418
|
+
console.log(`Path: ${result.storageKey}`);
|
|
419
|
+
|
|
420
|
+
return result;
|
|
432
421
|
}
|
|
422
|
+
|
|
423
|
+
createBackup().catch(console.error);
|
|
433
424
|
```
|
|
434
425
|
|
|
435
|
-
|
|
426
|
+
**Backup Options:**
|
|
436
427
|
|
|
437
|
-
|
|
438
|
-
|
|
428
|
+
- `compress` - Enable/disable compression (default: from config)
|
|
429
|
+
- `encrypt` - Enable/disable encryption (default: from config)
|
|
430
|
+
- `format` - Backup format: `'custom'` (default), `'plain'`, `'directory'`, `'tar'`
|
|
431
|
+
- `type` - Backup type: `'full'` (default), `'schema'`, `'data'`
|
|
439
432
|
|
|
440
|
-
|
|
433
|
+
### Listing Backups
|
|
441
434
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
cron: '0 * * * *',
|
|
445
|
-
enabled: true,
|
|
446
|
-
});
|
|
435
|
+
```javascript
|
|
436
|
+
const { createDBDock, BackupService } = require('dbdock');
|
|
447
437
|
|
|
448
|
-
|
|
449
|
-
|
|
438
|
+
async function listBackups() {
|
|
439
|
+
const dbdock = await createDBDock();
|
|
440
|
+
const backupService = dbdock.get(BackupService);
|
|
450
441
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
});
|
|
442
|
+
const backups = await backupService.listBackups();
|
|
443
|
+
|
|
444
|
+
console.log(`Found ${backups.length} backups:`);
|
|
445
|
+
backups.forEach(
|
|
446
|
+
(backup: {
|
|
447
|
+
id: string;
|
|
448
|
+
formattedSize: string;
|
|
449
|
+
startTime: string | Date;
|
|
450
|
+
}) => {
|
|
451
|
+
console.log(
|
|
452
|
+
`- ${backup.id} (${backup.formattedSize}, created: ${backup.startTime})`
|
|
453
|
+
);
|
|
454
|
+
}
|
|
455
|
+
);
|
|
454
456
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
+
return backups;
|
|
458
|
+
}
|
|
457
459
|
|
|
458
|
-
|
|
460
|
+
listBackups().catch(console.error);
|
|
459
461
|
```
|
|
460
462
|
|
|
461
|
-
###
|
|
463
|
+
### Getting Backup Metadata
|
|
462
464
|
|
|
463
|
-
```
|
|
464
|
-
|
|
465
|
-
import { AppModule } from './app.module';
|
|
466
|
-
import { ScheduleManager } from 'dbdock';
|
|
465
|
+
```javascript
|
|
466
|
+
const { createDBDock, BackupService } = require('dbdock');
|
|
467
467
|
|
|
468
|
-
async function
|
|
469
|
-
const
|
|
468
|
+
async function getBackupInfo(backupId) {
|
|
469
|
+
const dbdock = await createDBDock();
|
|
470
|
+
const backupService = dbdock.get(BackupService);
|
|
470
471
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
472
|
+
const metadata = await backupService.getBackupMetadata(backupId);
|
|
473
|
+
|
|
474
|
+
if (!metadata) {
|
|
475
|
+
console.log('Backup not found');
|
|
476
|
+
return null;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
console.log('Backup details:', {
|
|
480
|
+
id: metadata.id,
|
|
481
|
+
size: metadata.size,
|
|
482
|
+
created: metadata.startTime,
|
|
483
|
+
encrypted: !!metadata.encryption,
|
|
484
|
+
compressed: metadata.compression.enabled,
|
|
475
485
|
});
|
|
486
|
+
|
|
487
|
+
return metadata;
|
|
488
|
+
}
|
|
476
489
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
490
|
+
getBackupInfo('your-backup-id').catch(console.error);
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
**Note:** Restore functionality is currently only available via CLI (`npx dbdock restore`). Programmatic restore will be available in a future release.
|
|
494
|
+
|
|
495
|
+
### Scheduling Backups
|
|
496
|
+
|
|
497
|
+
DBDock doesn't include a built-in scheduler (to keep the package lightweight), but it's easy to schedule backups using `node-cron`.
|
|
498
|
+
|
|
499
|
+
First, install `node-cron`:
|
|
500
|
+
|
|
501
|
+
```bash
|
|
502
|
+
npm install node-cron
|
|
503
|
+
npm install --save-dev @types/node-cron
|
|
504
|
+
```
|
|
482
505
|
|
|
483
|
-
|
|
484
|
-
await app.listen(3000);
|
|
506
|
+
Then create a scheduler script (e.g., `scheduler.ts`):
|
|
485
507
|
|
|
486
|
-
|
|
508
|
+
```typescript
|
|
509
|
+
import { createDBDock, BackupService } from 'dbdock';
|
|
510
|
+
import * as cron from 'node-cron';
|
|
511
|
+
|
|
512
|
+
async function startScheduler() {
|
|
513
|
+
// Initialize DBDock
|
|
514
|
+
const dbdock = await createDBDock();
|
|
515
|
+
const backupService = dbdock.get(BackupService);
|
|
516
|
+
|
|
517
|
+
console.log('🚀 Backup scheduler started. Running every minute...');
|
|
518
|
+
|
|
519
|
+
// Schedule task to run every minute ('* * * * *')
|
|
520
|
+
// For every 5 minutes use: '*/5 * * * *'
|
|
521
|
+
// For every hour use: '0 * * * *'
|
|
522
|
+
cron.schedule('* * * * *', async () => {
|
|
523
|
+
try {
|
|
524
|
+
console.log('\n⏳ Starting scheduled backup...');
|
|
525
|
+
|
|
526
|
+
const result = await backupService.createBackup({
|
|
527
|
+
format: 'plain', // Use 'plain' for SQL text, 'custom' for binary
|
|
528
|
+
compress: true,
|
|
529
|
+
encrypt: true,
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
console.log(`✅ Backup successful: ${result.metadata.id}`);
|
|
533
|
+
console.log(`📦 Size: ${result.metadata.formattedSize}`);
|
|
534
|
+
console.log(`📂 Path: ${result.storageKey}`);
|
|
535
|
+
} catch (error) {
|
|
536
|
+
console.error('❌ Backup failed:', error);
|
|
537
|
+
}
|
|
538
|
+
});
|
|
487
539
|
}
|
|
488
540
|
|
|
489
|
-
|
|
541
|
+
startScheduler().catch(console.error);
|
|
542
|
+
```
|
|
543
|
+
|
|
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.
|
|
545
|
+
|
|
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
|
+
|
|
550
|
+
**Configuration in `dbdock.config.json`:**
|
|
551
|
+
|
|
552
|
+
```json
|
|
553
|
+
{
|
|
554
|
+
"database": { ... },
|
|
555
|
+
"storage": { ... },
|
|
556
|
+
"backup": { ... },
|
|
557
|
+
"alerts": {
|
|
558
|
+
"email": {
|
|
559
|
+
"enabled": true,
|
|
560
|
+
"smtp": {
|
|
561
|
+
"host": "smtp.gmail.com",
|
|
562
|
+
"port": 587,
|
|
563
|
+
"secure": false,
|
|
564
|
+
"auth": {
|
|
565
|
+
"user": "your-email@gmail.com",
|
|
566
|
+
"pass": "your-app-password"
|
|
567
|
+
}
|
|
568
|
+
},
|
|
569
|
+
"from": "backups@yourapp.com",
|
|
570
|
+
"to": ["admin@yourapp.com", "devops@yourapp.com"]
|
|
571
|
+
},
|
|
572
|
+
"slack": {
|
|
573
|
+
"enabled": true,
|
|
574
|
+
"webhookUrl": "https://hooks.slack.com/services/..."
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
```
|
|
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
|
+
|
|
587
|
+
**SMTP Provider Examples:**
|
|
588
|
+
|
|
589
|
+
_Gmail:_
|
|
590
|
+
```json
|
|
591
|
+
{
|
|
592
|
+
"smtp": {
|
|
593
|
+
"host": "smtp.gmail.com",
|
|
594
|
+
"port": 587,
|
|
595
|
+
"secure": false,
|
|
596
|
+
"auth": {
|
|
597
|
+
"user": "your-email@gmail.com",
|
|
598
|
+
"pass": "your-app-password"
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
> **Note:** For Gmail, you need to [create an App Password](https://support.google.com/accounts/answer/185833) instead of using your regular password.
|
|
605
|
+
|
|
606
|
+
_SendGrid:_
|
|
607
|
+
```json
|
|
608
|
+
{
|
|
609
|
+
"smtp": {
|
|
610
|
+
"host": "smtp.sendgrid.net",
|
|
611
|
+
"port": 587,
|
|
612
|
+
"secure": false,
|
|
613
|
+
"auth": {
|
|
614
|
+
"user": "apikey",
|
|
615
|
+
"pass": "YOUR_SENDGRID_API_KEY"
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
_AWS SES:_
|
|
622
|
+
```json
|
|
623
|
+
{
|
|
624
|
+
"smtp": {
|
|
625
|
+
"host": "email-smtp.us-east-1.amazonaws.com",
|
|
626
|
+
"port": 587,
|
|
627
|
+
"secure": false,
|
|
628
|
+
"auth": {
|
|
629
|
+
"user": "YOUR_SMTP_USERNAME",
|
|
630
|
+
"pass": "YOUR_SMTP_PASSWORD"
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
_Mailgun:_
|
|
637
|
+
```json
|
|
638
|
+
{
|
|
639
|
+
"smtp": {
|
|
640
|
+
"host": "smtp.mailgun.org",
|
|
641
|
+
"port": 587,
|
|
642
|
+
"secure": false,
|
|
643
|
+
"auth": {
|
|
644
|
+
"user": "postmaster@your-domain.mailgun.org",
|
|
645
|
+
"pass": "YOUR_MAILGUN_SMTP_PASSWORD"
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
**Using Alerts Programmatically:**
|
|
652
|
+
|
|
653
|
+
Once configured in `dbdock.config.json`, alerts are sent automatically when you create backups programmatically:
|
|
654
|
+
|
|
655
|
+
```javascript
|
|
656
|
+
const { createDBDock, BackupService } = require('dbdock');
|
|
657
|
+
|
|
658
|
+
async function createBackupWithAlerts() {
|
|
659
|
+
const dbdock = await createDBDock();
|
|
660
|
+
const backupService = dbdock.get(BackupService);
|
|
661
|
+
|
|
662
|
+
// Alerts will be sent automatically after backup completes
|
|
663
|
+
const result = await backupService.createBackup({
|
|
664
|
+
compress: true,
|
|
665
|
+
encrypt: true,
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
console.log(`Backup created: ${result.metadata.id}`);
|
|
669
|
+
// Alerts sent to configured channels
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
createBackupWithAlerts().catch(console.error);
|
|
490
673
|
```
|
|
674
|
+
|
|
675
|
+
**Alert Content:**
|
|
676
|
+
|
|
677
|
+
Success alerts include:
|
|
678
|
+
- Backup ID
|
|
679
|
+
- Database name
|
|
680
|
+
- Size (original and compressed)
|
|
681
|
+
- Duration
|
|
682
|
+
- Storage location
|
|
683
|
+
- Encryption status
|
|
684
|
+
|
|
685
|
+
Failure alerts include:
|
|
686
|
+
- Error message
|
|
687
|
+
- Database details
|
|
688
|
+
- Timestamp
|
|
689
|
+
- Helpful troubleshooting tips
|
|
690
|
+
|
|
691
|
+
**Testing Alert Configuration:**
|
|
692
|
+
|
|
693
|
+
Run `npx dbdock test` to validate your configuration without creating a backup.
|
|
694
|
+
|
|
695
|
+
**Important Notes:**
|
|
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)
|
|
491
703
|
|
|
492
704
|
## Requirements
|
|
493
705
|
|
|
@@ -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('
|
|
68
|
+
this.logger.log('Alerts disabled - no alerts configuration found');
|
|
65
69
|
return;
|
|
66
70
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
80
|
-
this.logger.
|
|
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
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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
|
-
|
|
170
|
-
|
|
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) {
|