dbdock 1.1.15 → 1.1.17
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 +473 -580
- package/dist/cli/commands/analyze.d.ts +1 -0
- package/dist/cli/commands/analyze.js +135 -0
- package/dist/cli/commands/analyze.js.map +1 -0
- package/dist/cli/commands/cross-migrate.d.ts +11 -0
- package/dist/cli/commands/cross-migrate.js +307 -0
- package/dist/cli/commands/cross-migrate.js.map +1 -0
- package/dist/cli/index.js +20 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/migration/analyzers/mongodb.analyzer.d.ts +3 -0
- package/dist/migration/analyzers/mongodb.analyzer.js +208 -0
- package/dist/migration/analyzers/mongodb.analyzer.js.map +1 -0
- package/dist/migration/analyzers/postgres.analyzer.d.ts +3 -0
- package/dist/migration/analyzers/postgres.analyzer.js +176 -0
- package/dist/migration/analyzers/postgres.analyzer.js.map +1 -0
- package/dist/migration/config.manager.d.ts +3 -0
- package/dist/migration/config.manager.js +80 -0
- package/dist/migration/config.manager.js.map +1 -0
- package/dist/migration/engines/migration.engine.d.ts +6 -0
- package/dist/migration/engines/migration.engine.js +62 -0
- package/dist/migration/engines/migration.engine.js.map +1 -0
- package/dist/migration/engines/mongo-to-postgres.engine.d.ts +2 -0
- package/dist/migration/engines/mongo-to-postgres.engine.js +409 -0
- package/dist/migration/engines/mongo-to-postgres.engine.js.map +1 -0
- package/dist/migration/engines/postgres-to-mongo.engine.d.ts +2 -0
- package/dist/migration/engines/postgres-to-mongo.engine.js +192 -0
- package/dist/migration/engines/postgres-to-mongo.engine.js.map +1 -0
- package/dist/migration/mappers/mongo-to-postgres.mapper.d.ts +2 -0
- package/dist/migration/mappers/mongo-to-postgres.mapper.js +263 -0
- package/dist/migration/mappers/mongo-to-postgres.mapper.js.map +1 -0
- package/dist/migration/mappers/postgres-to-mongo.mapper.d.ts +2 -0
- package/dist/migration/mappers/postgres-to-mongo.mapper.js +174 -0
- package/dist/migration/mappers/postgres-to-mongo.mapper.js.map +1 -0
- package/dist/migration/reference.detector.d.ts +2 -0
- package/dist/migration/reference.detector.js +57 -0
- package/dist/migration/reference.detector.js.map +1 -0
- package/dist/migration/type.mapper.d.ts +12 -0
- package/dist/migration/type.mapper.js +197 -0
- package/dist/migration/type.mapper.js.map +1 -0
- package/dist/migration/types.d.ts +183 -0
- package/dist/migration/types.js +11 -0
- package/dist/migration/types.js.map +1 -0
- package/package.json +11 -2
package/README.md
CHANGED
|
@@ -1,116 +1,117 @@
|
|
|
1
1
|
# DBDock
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Stop writing backup scripts. Stop losing sleep over database migrations. DBDock handles PostgreSQL backups, restores, database copies, and cross-database migrations between MongoDB and PostgreSQL in one command.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/dbdock)
|
|
6
6
|
[](LICENSE)
|
|
7
7
|
[](https://nodejs.org)
|
|
8
8
|
[](https://dbdock.mintlify.app)
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
[Full Docs](https://dbdock.mintlify.app) ・ [Discussions](https://github.com/naheemolaide/dbdock-support/discussions) ・ [Report a Bug](https://github.com/naheemolaide/dbdock-support/issues)
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## The Problem
|
|
15
|
+
|
|
16
|
+
Every time you need to backup a database, copy it to staging, or restore before a migration — it's the same boring steps. Connect, dump, upload, move files around, remember the right flags. Sure, you could ask AI to write you a script. But then you're maintaining that script, handling errors, adding encryption, switching storage providers, doing it again next week.
|
|
17
|
+
|
|
18
|
+
It's not hard. It's just repetitive. And repetitive stuff should be one command.
|
|
19
|
+
|
|
20
|
+
## The Fix
|
|
13
21
|
|
|
14
22
|
```bash
|
|
15
|
-
npx dbdock init
|
|
16
|
-
npx dbdock backup
|
|
17
|
-
npx dbdock restore
|
|
23
|
+
npx dbdock init # One-time setup (takes 30 seconds)
|
|
24
|
+
npx dbdock backup # Backup with encryption + compression
|
|
25
|
+
npx dbdock restore # Restore from any backup
|
|
26
|
+
npx dbdock copydb "db_url_1" "db_url_2" # Copy entire database, zero config
|
|
27
|
+
npx dbdock migrate "mongo_url" "postgres_url" # Cross-database migration
|
|
18
28
|
```
|
|
19
29
|
|
|
20
|
-
|
|
30
|
+
That's it. No shell scripts. No manual uploads. No throwaway migration code.
|
|
21
31
|
|
|
22
|
-
|
|
23
|
-
- **Multiple Storage** - Local, AWS S3, Cloudflare R2, Cloudinary
|
|
24
|
-
- **Security First** - Hybrid config (env vars for secrets), AES-256 encryption, credential masking, .pgpass support
|
|
25
|
-
- **Retention Policies** - Automatic cleanup by count/age with safety nets
|
|
26
|
-
- **Smart UX** - Intelligent filtering for 100+ backups, clear error messages
|
|
27
|
-
- **Alerts** - Email (SMTP) and Slack notifications for backups (CLI & Programmatic)
|
|
28
|
-
- **TypeScript Native** - Full type safety for programmatic usage
|
|
29
|
-
- **Automation** - Cron schedules, auto-cleanup after backups
|
|
30
|
-
- **Migration Tool** - One command to migrate legacy configs to secure env vars
|
|
32
|
+
---
|
|
31
33
|
|
|
32
|
-
##
|
|
34
|
+
## Install
|
|
33
35
|
|
|
34
|
-
**
|
|
36
|
+
**Use directly with npx (no install needed):**
|
|
35
37
|
|
|
36
38
|
```bash
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
dbdock init # Use directly
|
|
40
|
-
dbdock backup
|
|
41
|
-
dbdock status
|
|
39
|
+
npx dbdock backup
|
|
42
40
|
```
|
|
43
41
|
|
|
44
|
-
**Or
|
|
42
|
+
**Or install globally:**
|
|
45
43
|
|
|
46
44
|
```bash
|
|
47
|
-
|
|
48
|
-
npx dbdock backup
|
|
49
|
-
npx dbdock status
|
|
45
|
+
npm install -g dbdock
|
|
50
46
|
```
|
|
51
47
|
|
|
52
|
-
|
|
48
|
+
**Prerequisites:** Node.js 18+ and PostgreSQL client tools (`pg_dump`, `pg_restore`, `psql`).
|
|
53
49
|
|
|
54
|
-
|
|
50
|
+
```bash
|
|
51
|
+
# macOS
|
|
52
|
+
brew install postgresql
|
|
55
53
|
|
|
56
|
-
|
|
54
|
+
# Ubuntu/Debian
|
|
55
|
+
sudo apt-get install postgresql-client
|
|
56
|
+
```
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
- Storage provider (Local, S3, R2, Cloudinary)
|
|
60
|
-
- Encryption/compression settings
|
|
61
|
-
- Email and Slack alerts (optional)
|
|
58
|
+
---
|
|
62
59
|
|
|
63
|
-
|
|
64
|
-
- Saves non-sensitive config to `dbdock.config.json` (safe for git)
|
|
65
|
-
- Saves secrets to `.env` (automatically gitignored)
|
|
66
|
-
- Auto-updates `.gitignore` to exclude sensitive files
|
|
60
|
+
## Commands
|
|
67
61
|
|
|
68
|
-
### `dbdock
|
|
62
|
+
### `dbdock init` — Set up in 30 seconds
|
|
69
63
|
|
|
70
|
-
|
|
64
|
+
Run once. It walks you through everything interactively:
|
|
71
65
|
|
|
72
66
|
```bash
|
|
73
|
-
npx dbdock
|
|
67
|
+
npx dbdock init
|
|
74
68
|
```
|
|
75
69
|
|
|
76
|
-
|
|
70
|
+
It asks for your database connection, picks your storage (Local, S3, R2, Cloudinary), sets up encryption if you want it, and optionally configures Slack/Email alerts.
|
|
71
|
+
|
|
72
|
+
**What happens under the hood:**
|
|
73
|
+
- Config (safe stuff) goes to `dbdock.config.json` — commit this
|
|
74
|
+
- Secrets go to `.env` — never committed, `.gitignore` updated automatically
|
|
77
75
|
|
|
78
|
-
|
|
76
|
+
---
|
|
79
77
|
|
|
80
|
-
|
|
78
|
+
### `dbdock backup` — One command, full backup
|
|
81
79
|
|
|
80
|
+
```bash
|
|
81
|
+
npx dbdock backup
|
|
82
82
|
```
|
|
83
|
-
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
████████████████████ | 100% | 45.23 MB | Speed: 12.50 MB/s | Uploading to S3
|
|
84
86
|
✔ Backup completed successfully
|
|
85
87
|
```
|
|
86
88
|
|
|
87
|
-
|
|
89
|
+
Real-time progress. Streams directly to your storage provider. Done.
|
|
88
90
|
|
|
89
|
-
|
|
90
|
-
npx dbdock backup --encrypt --compress --compression-level 9
|
|
91
|
-
```
|
|
91
|
+
**Options:**
|
|
92
92
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
93
|
+
| Flag | What it does |
|
|
94
|
+
|------|------|
|
|
95
|
+
| `--encrypt` / `--no-encrypt` | Toggle AES-256 encryption |
|
|
96
|
+
| `--compress` / `--no-compress` | Toggle Brotli compression |
|
|
97
|
+
| `--encryption-key <key>` | Custom 64-char hex key |
|
|
98
|
+
| `--compression-level <1-11>` | Compression intensity (default: 6) |
|
|
97
99
|
|
|
98
|
-
**
|
|
100
|
+
**Need an encryption key?**
|
|
99
101
|
|
|
100
102
|
```bash
|
|
101
103
|
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
|
|
102
104
|
```
|
|
103
105
|
|
|
104
|
-
**Backup
|
|
106
|
+
**Backup formats:** `custom` (default binary), `plain` (SQL text), `directory`, `tar`
|
|
105
107
|
|
|
106
|
-
|
|
107
|
-
- `plain` - Plain SQL text format (.sql)
|
|
108
|
-
- `directory` - Directory format (.dir)
|
|
109
|
-
- `tar` - Tar archive format (.tar)
|
|
108
|
+
---
|
|
110
109
|
|
|
111
|
-
### `
|
|
110
|
+
### `dbdock restore` — Interactive restore with smart filtering
|
|
112
111
|
|
|
113
|
-
|
|
112
|
+
```bash
|
|
113
|
+
npx dbdock restore
|
|
114
|
+
```
|
|
114
115
|
|
|
115
116
|
```
|
|
116
117
|
Progress:
|
|
@@ -123,282 +124,385 @@ Progress:
|
|
|
123
124
|
✔ All steps completed in 8.42s
|
|
124
125
|
```
|
|
125
126
|
|
|
126
|
-
|
|
127
|
+
Got 200+ backups? It auto-enables smart filtering — search by date, keyword, or just grab the most recent ones. No scrolling through walls of text.
|
|
128
|
+
|
|
129
|
+
You can also restore to a completely different database. Pick "New Database Instance (Migrate)" when prompted and enter the target connection details.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
### `dbdock copydb` — Copy a database with just two URLs
|
|
134
|
+
|
|
135
|
+
This is the one people love. No config files. No setup. Just paste two PostgreSQL URLs:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
npx dbdock copydb "postgresql://user:pass@source:5432/mydb" "postgresql://user:pass@target:5432/mydb"
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
It tests both connections, shows you the source DB size and table count, warns you if the target has existing data, and asks for confirmation before doing anything. Then it streams `pg_dump` directly into `pg_restore` — no temp files, no waiting.
|
|
142
|
+
|
|
143
|
+
**Options:**
|
|
144
|
+
|
|
145
|
+
| Flag | What it does |
|
|
146
|
+
|------|------|
|
|
147
|
+
| `--schema-only` | Copy tables, indexes, constraints — no data |
|
|
148
|
+
| `--data-only` | Copy data only (schema must exist on target) |
|
|
149
|
+
| `--verbose` | Show detailed pg_dump/pg_restore output |
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
npx dbdock copydb --schema-only "source_url" "target_url"
|
|
153
|
+
npx dbdock copydb --data-only "source_url" "target_url"
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**Perfect for:**
|
|
157
|
+
- Moving between Neon, Supabase, Railway, RDS, or any Postgres host
|
|
158
|
+
- Migrating cloud providers without the headache
|
|
127
159
|
|
|
128
|
-
|
|
129
|
-
- Date range (24h, 7d, 30d, 90d, custom)
|
|
130
|
-
- Search by keyword/ID
|
|
160
|
+
**Environment consolidation:**
|
|
131
161
|
|
|
132
|
-
|
|
162
|
+
```bash
|
|
163
|
+
# Refresh staging with production data
|
|
164
|
+
npx dbdock copydb "prod_url" "staging_url"
|
|
133
165
|
|
|
134
|
-
|
|
166
|
+
# Promote staging to production
|
|
167
|
+
npx dbdock copydb "staging_url" "prod_url"
|
|
135
168
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
3. Choose "New Database Instance (Migrate)"
|
|
139
|
-
4. Enter connection details for the target database
|
|
169
|
+
# Pull production to local for debugging
|
|
170
|
+
npx dbdock copydb "prod_url" "postgresql://postgres:pass@localhost:5432/myapp"
|
|
140
171
|
|
|
141
|
-
|
|
172
|
+
# Align schema across environments without touching data
|
|
173
|
+
npx dbdock copydb --schema-only "prod_url" "staging_url"
|
|
174
|
+
```
|
|
142
175
|
|
|
143
|
-
|
|
176
|
+
---
|
|
144
177
|
|
|
145
|
-
|
|
178
|
+
### `dbdock list` — See all your backups
|
|
146
179
|
|
|
147
180
|
```bash
|
|
148
|
-
npx dbdock list #
|
|
181
|
+
npx dbdock list # Everything
|
|
149
182
|
npx dbdock list --recent 10 # Last 10
|
|
150
|
-
npx dbdock list --search keyword #
|
|
183
|
+
npx dbdock list --search keyword # Find specific backup
|
|
151
184
|
npx dbdock list --days 7 # Last 7 days
|
|
152
185
|
```
|
|
153
186
|
|
|
154
|
-
|
|
187
|
+
Auto-filters when you have 50+ backups so the output stays clean.
|
|
155
188
|
|
|
156
|
-
|
|
189
|
+
---
|
|
157
190
|
|
|
158
|
-
|
|
191
|
+
### `dbdock delete` — Remove backups
|
|
159
192
|
|
|
160
193
|
```bash
|
|
161
|
-
npx dbdock delete # Interactive
|
|
162
|
-
npx dbdock delete --key <id> #
|
|
163
|
-
npx dbdock delete --all #
|
|
194
|
+
npx dbdock delete # Interactive picker
|
|
195
|
+
npx dbdock delete --key <id> # Delete specific backup
|
|
196
|
+
npx dbdock delete --all # Nuke everything (with confirmation)
|
|
164
197
|
```
|
|
165
198
|
|
|
166
|
-
|
|
199
|
+
---
|
|
167
200
|
|
|
168
|
-
|
|
201
|
+
### `dbdock cleanup` — Auto-clean old backups
|
|
169
202
|
|
|
170
203
|
```bash
|
|
171
204
|
npx dbdock cleanup # Interactive with preview
|
|
172
|
-
npx dbdock cleanup --dry-run #
|
|
205
|
+
npx dbdock cleanup --dry-run # See what would be deleted
|
|
173
206
|
npx dbdock cleanup --force # Skip confirmation
|
|
174
207
|
```
|
|
175
208
|
|
|
176
|
-
Shows
|
|
209
|
+
Shows you exactly what gets deleted and how much space you reclaim before doing anything.
|
|
177
210
|
|
|
178
|
-
|
|
211
|
+
---
|
|
179
212
|
|
|
180
|
-
|
|
213
|
+
### `dbdock status` — Check schedules and service health
|
|
181
214
|
|
|
182
215
|
```bash
|
|
183
|
-
dbdock status
|
|
216
|
+
npx dbdock status
|
|
184
217
|
```
|
|
185
218
|
|
|
186
|
-
**Output:**
|
|
187
|
-
|
|
188
219
|
```
|
|
189
|
-
📅 Scheduled Backups:
|
|
190
|
-
|
|
191
220
|
┌─────┬──────────────┬─────────────────┬──────────┐
|
|
192
221
|
│ # │ Name │ Cron Expression │ Status │
|
|
193
222
|
├─────┼──────────────┼─────────────────┼──────────┤
|
|
194
223
|
│ 1 │ daily │ 0 * * * * │ ✓ Active │
|
|
195
224
|
│ 2 │ weekly │ 0 0 * * 0 │ ✗ Paused │
|
|
196
225
|
└─────┴──────────────┴─────────────────┴──────────┘
|
|
226
|
+
```
|
|
197
227
|
|
|
198
|
-
|
|
228
|
+
---
|
|
199
229
|
|
|
200
|
-
|
|
230
|
+
### `dbdock schedule` — Manage cron schedules
|
|
201
231
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
Uptime: 2d 5h
|
|
205
|
-
Memory: 45.23 MB
|
|
232
|
+
```bash
|
|
233
|
+
npx dbdock schedule
|
|
206
234
|
```
|
|
207
235
|
|
|
208
|
-
|
|
236
|
+
Add, remove, or toggle backup schedules. Comes with presets (hourly, daily at midnight, daily at 2 AM, weekly, monthly) or use a custom cron expression.
|
|
209
237
|
|
|
210
|
-
|
|
238
|
+
**Heads up:** Schedules only run when DBDock is integrated into your Node.js app (see [Programmatic Usage](#programmatic-usage) below). The CLI just manages the config.
|
|
211
239
|
|
|
212
|
-
|
|
240
|
+
---
|
|
213
241
|
|
|
214
|
-
|
|
242
|
+
### `dbdock test` — Verify everything works
|
|
215
243
|
|
|
216
244
|
```bash
|
|
217
|
-
dbdock
|
|
245
|
+
npx dbdock test
|
|
218
246
|
```
|
|
219
247
|
|
|
220
|
-
|
|
248
|
+
Tests your database connection, storage provider, and alert config. Run this first if something feels off.
|
|
221
249
|
|
|
222
|
-
|
|
223
|
-
- Add new schedule with cron expression presets
|
|
224
|
-
- Remove or toggle (enable/disable) schedules
|
|
225
|
-
- Saves to `dbdock.config.json`
|
|
250
|
+
---
|
|
226
251
|
|
|
227
|
-
|
|
252
|
+
### `dbdock migrate-config` — Fix legacy configs
|
|
228
253
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
- Every week (Sunday): `0 0 * * 0`
|
|
233
|
-
- Every month (1st): `0 0 1 * *`
|
|
234
|
-
- Custom cron expression
|
|
254
|
+
```bash
|
|
255
|
+
npx dbdock migrate-config
|
|
256
|
+
```
|
|
235
257
|
|
|
236
|
-
|
|
258
|
+
Got secrets sitting in `dbdock.config.json` from an older version? This extracts them to `.env`, cleans up your config, and updates `.gitignore`. One command, done.
|
|
237
259
|
|
|
238
|
-
|
|
260
|
+
---
|
|
239
261
|
|
|
240
|
-
|
|
262
|
+
## Cross-Database Migration (MongoDB ↔ PostgreSQL)
|
|
241
263
|
|
|
242
|
-
DBDock
|
|
264
|
+
Move your data between MongoDB and PostgreSQL without writing throwaway scripts. DBDock analyzes the source, proposes a schema mapping, lets you review it, and handles the transfer.
|
|
243
265
|
|
|
244
|
-
|
|
245
|
-
- **Sensitive secrets** → Environment variables (NEVER commit to git)
|
|
266
|
+
### `dbdock analyze` — Understand your database first
|
|
246
267
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
268
|
+
```bash
|
|
269
|
+
npx dbdock analyze "mongodb://localhost:27017/myapp"
|
|
270
|
+
npx dbdock analyze "postgresql://user:pass@localhost:5432/myapp"
|
|
271
|
+
```
|
|
251
272
|
|
|
252
|
-
|
|
273
|
+
Scans the source database and shows you everything — collections/tables, field types, nested structures, inconsistencies (like a `price` field that's a string in 200 docs and a number in 15,000), and missing fields.
|
|
253
274
|
|
|
254
|
-
|
|
275
|
+
```
|
|
276
|
+
DBDock - Database Analysis
|
|
277
|
+
─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
|
278
|
+
|
|
279
|
+
ℹ Database: MongoDB — myapp
|
|
280
|
+
ℹ Host: localhost:27017
|
|
281
|
+
|
|
282
|
+
✓ Analysis complete
|
|
283
|
+
|
|
284
|
+
Found 4 collections, 57.2K total documents
|
|
255
285
|
|
|
256
|
-
|
|
286
|
+
users (45K docs)
|
|
287
|
+
├─ _id (objectId)
|
|
288
|
+
├─ name (string)
|
|
289
|
+
├─ email (string)
|
|
290
|
+
├─ address (object)
|
|
291
|
+
│ ├─ street (string)
|
|
292
|
+
│ ├─ city (string)
|
|
293
|
+
│ └─ zip (string)
|
|
294
|
+
├─ phone (string) [62% present]
|
|
295
|
+
└─ orders (array: object)
|
|
296
|
+
|
|
297
|
+
products (12K docs)
|
|
298
|
+
├─ _id (objectId)
|
|
299
|
+
├─ title (string)
|
|
300
|
+
├─ price (string(203), number(11797)) ⚠ mixed types
|
|
301
|
+
├─ tags (array: string)
|
|
302
|
+
└─ meta (object)
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
### `dbdock migrate` — Cross-database migration
|
|
257
308
|
|
|
258
309
|
```bash
|
|
259
|
-
|
|
260
|
-
|
|
310
|
+
npx dbdock migrate "mongodb://localhost:27017/myapp" "postgresql://user:pass@localhost:5432/myapp"
|
|
311
|
+
```
|
|
261
312
|
|
|
262
|
-
|
|
263
|
-
DBDOCK_STORAGE_ACCESS_KEY=your-access-key
|
|
264
|
-
DBDOCK_STORAGE_SECRET_KEY=your-secret-key
|
|
313
|
+
DBDock analyzes the source, generates a schema mapping proposal, and presents it for review before touching anything:
|
|
265
314
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
315
|
+
```
|
|
316
|
+
DBDock - Cross-Database Migration
|
|
317
|
+
─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
|
269
318
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
DBDOCK_SMTP_PASS=your-app-password
|
|
319
|
+
ℹ Source: MongoDB — myapp
|
|
320
|
+
ℹ Target: PostgreSQL — myapp
|
|
273
321
|
|
|
274
|
-
|
|
275
|
-
|
|
322
|
+
✓ Source analyzed: 4 collections, 57.2K documents
|
|
323
|
+
|
|
324
|
+
Proposed Schema Mapping:
|
|
325
|
+
|
|
326
|
+
users → users
|
|
327
|
+
├─ _id → (uuid_from_objectid) users.id (uuid) PK
|
|
328
|
+
├─ name → users.name (text)
|
|
329
|
+
├─ email → users.email (text)
|
|
330
|
+
├─ address {} → user_addresses (1:1 relation)
|
|
331
|
+
│ ├─ street → street (text)
|
|
332
|
+
│ ├─ city → city (text)
|
|
333
|
+
│ └─ zip → zip (text)
|
|
334
|
+
├─ orders [] → user_orders child table
|
|
335
|
+
|
|
336
|
+
products → products
|
|
337
|
+
├─ _id → (uuid_from_objectid) products.id (uuid) PK
|
|
338
|
+
├─ title → products.title (text)
|
|
339
|
+
├─ price → products.price (numeric) nullable
|
|
340
|
+
├─ tags [] → product_tags text[]
|
|
341
|
+
├─ meta {} → products.meta (jsonb)
|
|
342
|
+
|
|
343
|
+
⚠ Conflicts Found:
|
|
344
|
+
|
|
345
|
+
• products.price: string in 203 docs, number in 11797 docs
|
|
346
|
+
→ Suggestion: cast to numeric, log failures
|
|
347
|
+
|
|
348
|
+
• users.phone: missing in 38.0% of documents
|
|
349
|
+
→ Suggestion: nullable column
|
|
350
|
+
|
|
351
|
+
? Accept mapping? (Y / export / cancel)
|
|
276
352
|
```
|
|
277
353
|
|
|
278
|
-
|
|
354
|
+
You review the mapping, then choose: execute it, export it as a config file to tweak, or cancel.
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
### What it handles
|
|
279
359
|
|
|
280
|
-
|
|
360
|
+
| Scenario | How DBDock handles it |
|
|
361
|
+
|---|---|
|
|
362
|
+
| **Nested objects** | Consistent shape → flattened to a related table. Varying/messy → kept as `jsonb` column |
|
|
363
|
+
| **Arrays of primitives** | `tags: ["a", "b"]` → PostgreSQL array column or junction table |
|
|
364
|
+
| **Arrays of objects** | `orders: [{item, qty}]` → child table with foreign key back to parent |
|
|
365
|
+
| **Missing fields** | Detects frequency across all documents, makes sparse fields nullable |
|
|
366
|
+
| **Type mismatches** | Same field with different types → casts to majority type, logs failures to `_migration_errors` |
|
|
367
|
+
| **ObjectId references** | Auto-detects `userId: ObjectId(...)` patterns → creates proper foreign keys |
|
|
368
|
+
| **Deep nesting** | Flattens up to configurable depth (default: 2), stores deeper levels as `jsonb` |
|
|
369
|
+
| **Postgres → Mongo** | 1:1 joins → embedded objects. Small 1:many → embedded arrays. Large 1:many → separate collections with refs |
|
|
370
|
+
| **Many-to-many** | Junction tables → arrays of values in the document |
|
|
371
|
+
|
|
372
|
+
---
|
|
373
|
+
|
|
374
|
+
### The reverse — PostgreSQL to MongoDB
|
|
281
375
|
|
|
282
376
|
```bash
|
|
283
|
-
npx dbdock migrate
|
|
377
|
+
npx dbdock migrate "postgresql://user:pass@localhost:5432/myapp" "mongodb://localhost:27017/myapp"
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
DBDock detects table relationships and proposes embedding vs referencing strategies:
|
|
381
|
+
|
|
382
|
+
```
|
|
383
|
+
Proposed Document Mapping:
|
|
384
|
+
|
|
385
|
+
users → users collection
|
|
386
|
+
├─ id → _id
|
|
387
|
+
├─ name → name
|
|
388
|
+
├─ email → email
|
|
389
|
+
├─ addresses → embed object as address
|
|
390
|
+
├─ orders → embed array as orders (< 1000 rows)
|
|
391
|
+
|
|
392
|
+
products → products collection
|
|
393
|
+
├─ id → _id
|
|
394
|
+
├─ title → title
|
|
395
|
+
├─ price → price
|
|
396
|
+
├─ product_tags → embed as tags array
|
|
284
397
|
```
|
|
285
398
|
|
|
286
|
-
|
|
287
|
-
1. Extract all secrets from your config file
|
|
288
|
-
2. Create/update `.env` with the secrets
|
|
289
|
-
3. Remove secrets from `dbdock.config.json`
|
|
290
|
-
4. Update `.gitignore` automatically
|
|
399
|
+
Small 1:1 and 1:many relations get embedded. Large 1:many relations stay as separate collections with reference fields. You can override per table.
|
|
291
400
|
|
|
292
|
-
|
|
401
|
+
---
|
|
293
402
|
|
|
294
|
-
|
|
403
|
+
### Dry run & validation
|
|
295
404
|
|
|
296
405
|
```bash
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
406
|
+
npx dbdock migrate --dry-run "mongodb://..." "postgresql://..."
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
Runs the full migration into a temporary schema, validates counts and referential integrity, then cleans up. Nothing touches the real target:
|
|
300
410
|
|
|
301
|
-
# Add your connection (format: host:port:database:username:password)
|
|
302
|
-
echo "localhost:5432:myapp:postgres:my-secure-password" >> ~/.pgpass
|
|
303
411
|
```
|
|
412
|
+
Dry Run Results:
|
|
413
|
+
─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
|
414
|
+
✔ users: 45,000 → 45,000
|
|
415
|
+
✔ addresses: 45,000 → 44,998 (2 failed)
|
|
416
|
+
✔ orders: 312,400 → 312,400
|
|
417
|
+
✔ products: 12,000 → 12,000
|
|
418
|
+
✔ product_tags: 38,200 → 38,200
|
|
304
419
|
|
|
305
|
-
|
|
420
|
+
⚠ 2 rows failed (see _migration_errors)
|
|
421
|
+
✓ All foreign keys valid — dry run passed
|
|
422
|
+
```
|
|
306
423
|
|
|
307
|
-
|
|
424
|
+
---
|
|
308
425
|
|
|
309
|
-
|
|
310
|
-
- **File permission checking** - Warns about insecure config file permissions
|
|
311
|
-
- **Encryption at rest** - AES-256-GCM encryption for backups
|
|
312
|
-
- **Strict mode** - Optional enforcement of env-only secrets (`DBDOCK_STRICT_MODE=true`)
|
|
426
|
+
### Incremental / delta migration
|
|
313
427
|
|
|
314
|
-
|
|
428
|
+
Already migrated once but your app is still running on the old database? Sync only new or changed data:
|
|
315
429
|
|
|
316
|
-
|
|
430
|
+
```bash
|
|
431
|
+
npx dbdock migrate --incremental --since "2026-03-10" "mongodb://..." "postgresql://..."
|
|
432
|
+
```
|
|
317
433
|
|
|
318
|
-
|
|
434
|
+
Uses `createdAt`/`updatedAt` timestamps to only transfer new and changed documents. Existing rows in the target are upserted.
|
|
319
435
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
"database": "myapp"
|
|
329
|
-
},
|
|
330
|
-
"storage": {
|
|
331
|
-
"provider": "s3",
|
|
332
|
-
"s3": {
|
|
333
|
-
"bucket": "my-backups",
|
|
334
|
-
"region": "us-east-1"
|
|
335
|
-
}
|
|
336
|
-
},
|
|
337
|
-
"backup": {
|
|
338
|
-
"format": "custom",
|
|
339
|
-
"compression": {
|
|
340
|
-
"enabled": true,
|
|
341
|
-
"level": 6
|
|
342
|
-
},
|
|
343
|
-
"encryption": {
|
|
344
|
-
"enabled": true
|
|
345
|
-
},
|
|
346
|
-
"retention": {
|
|
347
|
-
"enabled": true,
|
|
348
|
-
"maxBackups": 100,
|
|
349
|
-
"maxAgeDays": 30,
|
|
350
|
-
"minBackups": 5,
|
|
351
|
-
"runAfterBackup": true
|
|
352
|
-
}
|
|
353
|
-
},
|
|
354
|
-
"alerts": {
|
|
355
|
-
"email": {
|
|
356
|
-
"enabled": true,
|
|
357
|
-
"smtp": {
|
|
358
|
-
"host": "smtp.gmail.com",
|
|
359
|
-
"port": 587,
|
|
360
|
-
"secure": false
|
|
361
|
-
},
|
|
362
|
-
"from": "backups@yourapp.com",
|
|
363
|
-
"to": ["admin@yourapp.com"]
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
}
|
|
436
|
+
---
|
|
437
|
+
|
|
438
|
+
### Save & reuse config
|
|
439
|
+
|
|
440
|
+
Export the mapping for your team to use reproducibly:
|
|
441
|
+
|
|
442
|
+
```bash
|
|
443
|
+
npx dbdock migrate --export-config ./migration.yaml "mongodb://..." "postgresql://..."
|
|
367
444
|
```
|
|
368
445
|
|
|
369
|
-
|
|
446
|
+
Then anyone runs the same migration:
|
|
447
|
+
|
|
448
|
+
```bash
|
|
449
|
+
npx dbdock migrate --config ./migration.yaml "mongodb://..." "postgresql://..."
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
Tweak the YAML/JSON config to adjust type mappings, embedding strategies, or rename target tables — then re-run.
|
|
453
|
+
|
|
454
|
+
---
|
|
455
|
+
|
|
456
|
+
### Migration options
|
|
457
|
+
|
|
458
|
+
| Flag | What it does |
|
|
459
|
+
|---|---|
|
|
460
|
+
| `--dry-run` | Migrate into temp schema, validate, clean up |
|
|
461
|
+
| `--incremental` | Only sync new/changed data |
|
|
462
|
+
| `--since <date>` | Cutoff date for incremental mode (ISO format) |
|
|
463
|
+
| `--config <path>` | Load a saved migration config (YAML or JSON) |
|
|
464
|
+
| `--export-config <path>` | Save migration plan to file without executing |
|
|
465
|
+
| `--batch-size <n>` | Documents per batch (default: 1000) |
|
|
466
|
+
| `--max-depth <n>` | Max nesting depth before storing as jsonb (default: 2) |
|
|
467
|
+
|
|
468
|
+
### Core principles
|
|
469
|
+
|
|
470
|
+
- **Never lose data** — failed rows go to `_migration_errors`, never silently dropped
|
|
471
|
+
- **Never surprise the user** — full mapping shown before execution, conflicts flagged
|
|
472
|
+
- **Always let you review** — nothing executes without explicit confirmation
|
|
473
|
+
- **Reproducible** — export/import configs for team-wide consistency
|
|
474
|
+
|
|
475
|
+
---
|
|
370
476
|
|
|
371
|
-
|
|
477
|
+
## Storage Providers
|
|
372
478
|
|
|
373
|
-
|
|
479
|
+
Pick your storage during `dbdock init`, or set it in `dbdock.config.json`:
|
|
480
|
+
|
|
481
|
+
### Local
|
|
374
482
|
|
|
375
483
|
```json
|
|
376
484
|
{ "storage": { "provider": "local", "local": { "path": "./backups" } } }
|
|
377
485
|
```
|
|
378
486
|
|
|
379
|
-
|
|
487
|
+
### AWS S3
|
|
380
488
|
|
|
381
489
|
```json
|
|
382
490
|
{
|
|
383
491
|
"storage": {
|
|
384
492
|
"provider": "s3",
|
|
385
|
-
"s3": {
|
|
386
|
-
"bucket": "my-backups",
|
|
387
|
-
"region": "us-east-1"
|
|
388
|
-
}
|
|
493
|
+
"s3": { "bucket": "my-backups", "region": "us-east-1" }
|
|
389
494
|
}
|
|
390
495
|
}
|
|
391
496
|
```
|
|
392
497
|
|
|
393
|
-
Set credentials via environment variables:
|
|
394
498
|
```bash
|
|
395
499
|
DBDOCK_STORAGE_ACCESS_KEY=your-access-key
|
|
396
500
|
DBDOCK_STORAGE_SECRET_KEY=your-secret-key
|
|
397
501
|
```
|
|
398
502
|
|
|
399
|
-
|
|
503
|
+
Needs IAM permissions: `s3:PutObject`, `s3:GetObject`, `s3:ListBucket`, `s3:DeleteObject`
|
|
400
504
|
|
|
401
|
-
|
|
505
|
+
### Cloudflare R2
|
|
402
506
|
|
|
403
507
|
```json
|
|
404
508
|
{
|
|
@@ -413,32 +517,70 @@ Required IAM permissions: `s3:PutObject`, `s3:GetObject`, `s3:ListBucket`, `s3:D
|
|
|
413
517
|
}
|
|
414
518
|
```
|
|
415
519
|
|
|
416
|
-
|
|
520
|
+
Same env vars as S3 above.
|
|
417
521
|
|
|
418
|
-
|
|
522
|
+
### Cloudinary
|
|
419
523
|
|
|
420
524
|
```json
|
|
421
525
|
{
|
|
422
526
|
"storage": {
|
|
423
527
|
"provider": "cloudinary",
|
|
424
|
-
"cloudinary": {
|
|
425
|
-
"cloudName": "your-cloud"
|
|
426
|
-
}
|
|
528
|
+
"cloudinary": { "cloudName": "your-cloud" }
|
|
427
529
|
}
|
|
428
530
|
}
|
|
429
531
|
```
|
|
430
532
|
|
|
431
|
-
Set credentials via environment variables:
|
|
432
533
|
```bash
|
|
433
534
|
DBDOCK_CLOUDINARY_API_KEY=your-api-key
|
|
434
535
|
DBDOCK_CLOUDINARY_API_SECRET=your-api-secret
|
|
435
536
|
```
|
|
436
537
|
|
|
437
|
-
|
|
538
|
+
---
|
|
539
|
+
|
|
540
|
+
## Security
|
|
541
|
+
|
|
542
|
+
DBDock splits your config into two parts by design:
|
|
543
|
+
|
|
544
|
+
| What | Where | Git safe? |
|
|
545
|
+
|------|-------|-----------|
|
|
546
|
+
| Host, port, bucket names, settings | `dbdock.config.json` | Yes |
|
|
547
|
+
| Passwords, API keys, secrets | `.env` | No (auto-gitignored) |
|
|
548
|
+
|
|
549
|
+
### Environment Variables
|
|
550
|
+
|
|
551
|
+
```bash
|
|
552
|
+
DBDOCK_DB_PASSWORD=your-database-password # Required
|
|
553
|
+
DBDOCK_STORAGE_ACCESS_KEY=your-access-key # For cloud storage
|
|
554
|
+
DBDOCK_STORAGE_SECRET_KEY=your-secret-key # For cloud storage
|
|
555
|
+
DBDOCK_ENCRYPTION_SECRET=64-char-hex-string # If encryption enabled
|
|
556
|
+
DBDOCK_SMTP_USER=your-email@example.com # For email alerts
|
|
557
|
+
DBDOCK_SMTP_PASS=your-app-password # For email alerts
|
|
558
|
+
DBDOCK_SLACK_WEBHOOK=https://hooks.slack.com/... # For Slack alerts
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
Reads from both `.env` and `.env.local` (`.env.local` takes priority).
|
|
438
562
|
|
|
439
|
-
###
|
|
563
|
+
### .pgpass Support
|
|
440
564
|
|
|
441
|
-
|
|
565
|
+
If you prefer `.pgpass` over env vars for the database password, DBDock detects and uses it automatically:
|
|
566
|
+
|
|
567
|
+
```bash
|
|
568
|
+
touch ~/.pgpass && chmod 600 ~/.pgpass
|
|
569
|
+
echo "localhost:5432:myapp:postgres:my-secure-password" >> ~/.pgpass
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
### What's Built In
|
|
573
|
+
|
|
574
|
+
- AES-256 encryption at rest
|
|
575
|
+
- Automatic credential masking in all logs
|
|
576
|
+
- File permission warnings for insecure configs
|
|
577
|
+
- Strict mode (`DBDOCK_STRICT_MODE=true`) — enforces env-only secrets
|
|
578
|
+
|
|
579
|
+
---
|
|
580
|
+
|
|
581
|
+
## Retention Policy
|
|
582
|
+
|
|
583
|
+
Backups pile up fast. Retention handles it automatically:
|
|
442
584
|
|
|
443
585
|
```json
|
|
444
586
|
{
|
|
@@ -454,449 +596,200 @@ Automatic cleanup to prevent storage bloat from frequent backups:
|
|
|
454
596
|
}
|
|
455
597
|
```
|
|
456
598
|
|
|
457
|
-
|
|
599
|
+
- Keeps at least `minBackups` recent backups (safety net — these never get deleted)
|
|
600
|
+
- Removes anything over `maxBackups` (oldest first)
|
|
601
|
+
- Removes anything older than `maxAgeDays`
|
|
602
|
+
- Runs automatically after each backup if `runAfterBackup` is on
|
|
603
|
+
- Or run manually: `npx dbdock cleanup`
|
|
458
604
|
|
|
459
|
-
|
|
460
|
-
- Deletes backups exceeding `maxBackups` limit (oldest first)
|
|
461
|
-
- Deletes backups older than `maxAgeDays` (respecting minBackups)
|
|
462
|
-
- Runs automatically after each backup (if `runAfterBackup: true`)
|
|
463
|
-
- Manual cleanup: `npx dbdock cleanup`
|
|
605
|
+
---
|
|
464
606
|
|
|
465
|
-
|
|
607
|
+
## Alerts
|
|
466
608
|
|
|
467
|
-
|
|
468
|
-
- Shows preview before deletion
|
|
469
|
-
- Detailed logging of what was deleted
|
|
470
|
-
- Error handling for failed deletions
|
|
609
|
+
Get notified on Slack or Email when backups succeed or fail. Set it up during `dbdock init` or add to your config:
|
|
471
610
|
|
|
472
|
-
|
|
611
|
+
```json
|
|
612
|
+
{
|
|
613
|
+
"alerts": {
|
|
614
|
+
"email": {
|
|
615
|
+
"enabled": true,
|
|
616
|
+
"smtp": {
|
|
617
|
+
"host": "smtp.gmail.com",
|
|
618
|
+
"port": 587,
|
|
619
|
+
"secure": false
|
|
620
|
+
},
|
|
621
|
+
"from": "backups@yourapp.com",
|
|
622
|
+
"to": ["admin@yourapp.com"]
|
|
623
|
+
},
|
|
624
|
+
"slack": {
|
|
625
|
+
"enabled": true,
|
|
626
|
+
"webhookUrl": "https://hooks.slack.com/services/..."
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
```
|
|
473
631
|
|
|
474
|
-
|
|
632
|
+
SMTP credentials go in env vars (`DBDOCK_SMTP_USER`, `DBDOCK_SMTP_PASS`).
|
|
475
633
|
|
|
476
|
-
|
|
634
|
+
**Works with:** Gmail, SendGrid, AWS SES, Mailgun — anything that speaks SMTP.
|
|
477
635
|
|
|
478
|
-
|
|
636
|
+
> For Gmail, use an [App Password](https://support.google.com/accounts/answer/185833), not your regular password.
|
|
479
637
|
|
|
480
|
-
|
|
481
|
-
npm install dbdock
|
|
482
|
-
```
|
|
638
|
+
Alerts fire automatically on both CLI and programmatic backups. They include backup ID, database name, size, duration, and storage location. Failure alerts include the error message and troubleshooting tips.
|
|
483
639
|
|
|
484
|
-
|
|
640
|
+
---
|
|
485
641
|
|
|
486
|
-
|
|
642
|
+
## Programmatic Usage
|
|
487
643
|
|
|
488
|
-
DBDock
|
|
644
|
+
Don't just use the CLI — drop DBDock into your Node.js app and trigger backups from code. Works with any backend (Express, Fastify, NestJS, whatever).
|
|
489
645
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
646
|
+
```bash
|
|
647
|
+
npm install dbdock
|
|
648
|
+
```
|
|
493
649
|
|
|
494
|
-
|
|
650
|
+
Make sure `dbdock.config.json` exists (run `npx dbdock init` first).
|
|
495
651
|
|
|
496
|
-
###
|
|
652
|
+
### Create a Backup
|
|
497
653
|
|
|
498
654
|
```javascript
|
|
499
655
|
const { createDBDock, BackupService } = require('dbdock');
|
|
500
656
|
|
|
501
|
-
async function
|
|
657
|
+
async function backup() {
|
|
502
658
|
const dbdock = await createDBDock();
|
|
503
659
|
const backupService = dbdock.get(BackupService);
|
|
504
660
|
|
|
505
661
|
const result = await backupService.createBackup({
|
|
506
|
-
format: 'plain',
|
|
662
|
+
format: 'plain',
|
|
507
663
|
compress: true,
|
|
508
664
|
encrypt: true,
|
|
509
665
|
});
|
|
510
666
|
|
|
511
|
-
console.log(`
|
|
512
|
-
console.log(`Size: ${result.metadata.formattedSize}`); // e.g. "108.3 KB"
|
|
513
|
-
console.log(`Path: ${result.storageKey}`);
|
|
514
|
-
|
|
515
|
-
return result;
|
|
667
|
+
console.log(`Done: ${result.metadata.id} (${result.metadata.formattedSize})`);
|
|
516
668
|
}
|
|
517
669
|
|
|
518
|
-
|
|
670
|
+
backup();
|
|
519
671
|
```
|
|
520
672
|
|
|
521
|
-
**
|
|
673
|
+
**Options:** `compress`, `encrypt`, `format` (`'custom'` | `'plain'` | `'directory'` | `'tar'`), `type` (`'full'` | `'schema'` | `'data'`)
|
|
522
674
|
|
|
523
|
-
|
|
524
|
-
- `encrypt` - Enable/disable encryption (default: from config)
|
|
525
|
-
- `format` - Backup format: `'custom'` (default), `'plain'`, `'directory'`, `'tar'`
|
|
526
|
-
- `type` - Backup type: `'full'` (default), `'schema'`, `'data'`
|
|
527
|
-
|
|
528
|
-
### Listing Backups
|
|
675
|
+
### List Backups
|
|
529
676
|
|
|
530
677
|
```javascript
|
|
531
678
|
const { createDBDock, BackupService } = require('dbdock');
|
|
532
679
|
|
|
533
|
-
async function
|
|
680
|
+
async function list() {
|
|
534
681
|
const dbdock = await createDBDock();
|
|
535
|
-
const
|
|
536
|
-
|
|
537
|
-
const backups = await backupService.listBackups();
|
|
538
|
-
|
|
539
|
-
console.log(`Found ${backups.length} backups:`);
|
|
540
|
-
backups.forEach(
|
|
541
|
-
(backup: {
|
|
542
|
-
id: string;
|
|
543
|
-
formattedSize: string;
|
|
544
|
-
startTime: string | Date;
|
|
545
|
-
}) => {
|
|
546
|
-
console.log(
|
|
547
|
-
`- ${backup.id} (${backup.formattedSize}, created: ${backup.startTime})`
|
|
548
|
-
);
|
|
549
|
-
}
|
|
550
|
-
);
|
|
551
|
-
|
|
552
|
-
return backups;
|
|
682
|
+
const backups = await dbdock.get(BackupService).listBackups();
|
|
683
|
+
backups.forEach(b => console.log(`${b.id} — ${b.formattedSize} — ${b.startTime}`));
|
|
553
684
|
}
|
|
554
685
|
|
|
555
|
-
|
|
686
|
+
list();
|
|
556
687
|
```
|
|
557
688
|
|
|
558
|
-
###
|
|
689
|
+
### Get Backup Info
|
|
559
690
|
|
|
560
691
|
```javascript
|
|
561
692
|
const { createDBDock, BackupService } = require('dbdock');
|
|
562
693
|
|
|
563
|
-
async function
|
|
694
|
+
async function info(id) {
|
|
564
695
|
const dbdock = await createDBDock();
|
|
565
|
-
const
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
if (!metadata) {
|
|
570
|
-
console.log('Backup not found');
|
|
571
|
-
return null;
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
console.log('Backup details:', {
|
|
575
|
-
id: metadata.id,
|
|
576
|
-
size: metadata.size,
|
|
577
|
-
created: metadata.startTime,
|
|
578
|
-
encrypted: !!metadata.encryption,
|
|
579
|
-
compressed: metadata.compression.enabled,
|
|
580
|
-
});
|
|
581
|
-
|
|
582
|
-
return metadata;
|
|
696
|
+
const metadata = await dbdock.get(BackupService).getBackupMetadata(id);
|
|
697
|
+
if (!metadata) return console.log('Not found');
|
|
698
|
+
console.log({ id: metadata.id, size: metadata.size, encrypted: !!metadata.encryption });
|
|
583
699
|
}
|
|
584
700
|
|
|
585
|
-
|
|
701
|
+
info('your-backup-id');
|
|
586
702
|
```
|
|
587
703
|
|
|
588
|
-
|
|
704
|
+
Restore is CLI-only for now (`npx dbdock restore`). Programmatic restore is coming.
|
|
589
705
|
|
|
590
|
-
###
|
|
706
|
+
### Schedule Backups with node-cron
|
|
591
707
|
|
|
592
|
-
DBDock
|
|
593
|
-
|
|
594
|
-
First, install `node-cron`:
|
|
708
|
+
DBDock stays lightweight — no built-in daemon. Use `node-cron` to schedule:
|
|
595
709
|
|
|
596
710
|
```bash
|
|
597
711
|
npm install node-cron
|
|
598
|
-
npm install --save-dev @types/node-cron
|
|
599
712
|
```
|
|
600
713
|
|
|
601
|
-
Then create a scheduler script (e.g., `scheduler.ts`):
|
|
602
|
-
|
|
603
714
|
```typescript
|
|
604
715
|
import { createDBDock, BackupService } from 'dbdock';
|
|
605
716
|
import * as cron from 'node-cron';
|
|
606
717
|
|
|
607
|
-
async function
|
|
608
|
-
// Initialize DBDock
|
|
718
|
+
async function start() {
|
|
609
719
|
const dbdock = await createDBDock();
|
|
610
720
|
const backupService = dbdock.get(BackupService);
|
|
611
721
|
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
// For every 5 minutes use: '*/5 * * * *'
|
|
616
|
-
// For every hour use: '0 * * * *'
|
|
617
|
-
cron.schedule('* * * * *', async () => {
|
|
618
|
-
try {
|
|
619
|
-
console.log('\n⏳ Starting scheduled backup...');
|
|
620
|
-
|
|
621
|
-
const result = await backupService.createBackup({
|
|
622
|
-
format: 'plain', // Use 'plain' for SQL text, 'custom' for binary
|
|
623
|
-
compress: true,
|
|
624
|
-
encrypt: true,
|
|
625
|
-
});
|
|
626
|
-
|
|
627
|
-
console.log(`✅ Backup successful: ${result.metadata.id}`);
|
|
628
|
-
console.log(`📦 Size: ${result.metadata.formattedSize}`);
|
|
629
|
-
console.log(`📂 Path: ${result.storageKey}`);
|
|
630
|
-
} catch (error) {
|
|
631
|
-
console.error('❌ Backup failed:', error);
|
|
632
|
-
}
|
|
722
|
+
cron.schedule('0 2 * * *', async () => {
|
|
723
|
+
const result = await backupService.createBackup({ compress: true, encrypt: true });
|
|
724
|
+
console.log(`Backup done: ${result.metadata.id}`);
|
|
633
725
|
});
|
|
726
|
+
|
|
727
|
+
console.log('Scheduler running — daily at 2 AM');
|
|
634
728
|
}
|
|
635
729
|
|
|
636
|
-
|
|
730
|
+
start();
|
|
637
731
|
```
|
|
638
732
|
|
|
639
|
-
|
|
733
|
+
---
|
|
640
734
|
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
DBDock can send notifications when backups complete (success or failure) via Email and Slack. Alerts work with both **programmatic usage** and **CLI commands**.
|
|
644
|
-
|
|
645
|
-
**Configuration in `dbdock.config.json`:**
|
|
646
|
-
|
|
647
|
-
```json
|
|
648
|
-
{
|
|
649
|
-
"database": { ... },
|
|
650
|
-
"storage": { ... },
|
|
651
|
-
"backup": { ... },
|
|
652
|
-
"alerts": {
|
|
653
|
-
"email": {
|
|
654
|
-
"enabled": true,
|
|
655
|
-
"smtp": {
|
|
656
|
-
"host": "smtp.gmail.com",
|
|
657
|
-
"port": 587,
|
|
658
|
-
"secure": false,
|
|
659
|
-
"auth": {
|
|
660
|
-
"user": "your-email@gmail.com",
|
|
661
|
-
"pass": "your-app-password"
|
|
662
|
-
}
|
|
663
|
-
},
|
|
664
|
-
"from": "backups@yourapp.com",
|
|
665
|
-
"to": ["admin@yourapp.com", "devops@yourapp.com"]
|
|
666
|
-
},
|
|
667
|
-
"slack": {
|
|
668
|
-
"enabled": true,
|
|
669
|
-
"webhookUrl": "https://hooks.slack.com/services/..."
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
```
|
|
674
|
-
|
|
675
|
-
**Slack Configuration:**
|
|
676
|
-
|
|
677
|
-
1. Create a Slack App or use an existing one.
|
|
678
|
-
2. Enable "Incoming Webhooks".
|
|
679
|
-
3. Create a new Webhook URL for your channel.
|
|
680
|
-
4. Run `npx dbdock init` and paste the URL when prompted.
|
|
681
|
-
|
|
682
|
-
**SMTP Provider Examples:**
|
|
683
|
-
|
|
684
|
-
_Gmail:_
|
|
685
|
-
```json
|
|
686
|
-
{
|
|
687
|
-
"smtp": {
|
|
688
|
-
"host": "smtp.gmail.com",
|
|
689
|
-
"port": 587,
|
|
690
|
-
"secure": false,
|
|
691
|
-
"auth": {
|
|
692
|
-
"user": "your-email@gmail.com",
|
|
693
|
-
"pass": "your-app-password"
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
```
|
|
698
|
-
|
|
699
|
-
> **Note:** For Gmail, you need to [create an App Password](https://support.google.com/accounts/answer/185833) instead of using your regular password.
|
|
700
|
-
|
|
701
|
-
_SendGrid:_
|
|
702
|
-
```json
|
|
703
|
-
{
|
|
704
|
-
"smtp": {
|
|
705
|
-
"host": "smtp.sendgrid.net",
|
|
706
|
-
"port": 587,
|
|
707
|
-
"secure": false,
|
|
708
|
-
"auth": {
|
|
709
|
-
"user": "apikey",
|
|
710
|
-
"pass": "YOUR_SENDGRID_API_KEY"
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
```
|
|
715
|
-
|
|
716
|
-
_AWS SES:_
|
|
717
|
-
```json
|
|
718
|
-
{
|
|
719
|
-
"smtp": {
|
|
720
|
-
"host": "email-smtp.us-east-1.amazonaws.com",
|
|
721
|
-
"port": 587,
|
|
722
|
-
"secure": false,
|
|
723
|
-
"auth": {
|
|
724
|
-
"user": "YOUR_SMTP_USERNAME",
|
|
725
|
-
"pass": "YOUR_SMTP_PASSWORD"
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
```
|
|
730
|
-
|
|
731
|
-
_Mailgun:_
|
|
732
|
-
```json
|
|
733
|
-
{
|
|
734
|
-
"smtp": {
|
|
735
|
-
"host": "smtp.mailgun.org",
|
|
736
|
-
"port": 587,
|
|
737
|
-
"secure": false,
|
|
738
|
-
"auth": {
|
|
739
|
-
"user": "postmaster@your-domain.mailgun.org",
|
|
740
|
-
"pass": "YOUR_MAILGUN_SMTP_PASSWORD"
|
|
741
|
-
}
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
```
|
|
745
|
-
|
|
746
|
-
**Using Alerts Programmatically:**
|
|
747
|
-
|
|
748
|
-
Once configured in `dbdock.config.json`, alerts are sent automatically when you create backups programmatically:
|
|
749
|
-
|
|
750
|
-
```javascript
|
|
751
|
-
const { createDBDock, BackupService } = require('dbdock');
|
|
752
|
-
|
|
753
|
-
async function createBackupWithAlerts() {
|
|
754
|
-
const dbdock = await createDBDock();
|
|
755
|
-
const backupService = dbdock.get(BackupService);
|
|
756
|
-
|
|
757
|
-
// Alerts will be sent automatically after backup completes
|
|
758
|
-
const result = await backupService.createBackup({
|
|
759
|
-
compress: true,
|
|
760
|
-
encrypt: true,
|
|
761
|
-
});
|
|
762
|
-
|
|
763
|
-
console.log(`Backup created: ${result.metadata.id}`);
|
|
764
|
-
// Alerts sent to configured channels
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
createBackupWithAlerts().catch(console.error);
|
|
768
|
-
```
|
|
769
|
-
|
|
770
|
-
**Alert Content:**
|
|
771
|
-
|
|
772
|
-
Success alerts include:
|
|
773
|
-
- Backup ID
|
|
774
|
-
- Database name
|
|
775
|
-
- Size (original and compressed)
|
|
776
|
-
- Duration
|
|
777
|
-
- Storage location
|
|
778
|
-
- Encryption status
|
|
779
|
-
|
|
780
|
-
Failure alerts include:
|
|
781
|
-
- Error message
|
|
782
|
-
- Database details
|
|
783
|
-
- Timestamp
|
|
784
|
-
- Helpful troubleshooting tips
|
|
785
|
-
|
|
786
|
-
**Testing Alert Configuration:**
|
|
787
|
-
|
|
788
|
-
Run `npx dbdock test` to validate your configuration without creating a backup.
|
|
789
|
-
|
|
790
|
-
**Important Notes:**
|
|
791
|
-
|
|
792
|
-
- ✅ Alerts work with programmatic usage (`createBackup()`)
|
|
793
|
-
- ✅ Alerts work with scheduled backups (cron jobs in your app)
|
|
794
|
-
- ✅ Alerts work with CLI commands (`npx dbdock backup`)
|
|
795
|
-
- Configuration is read from `dbdock.config.json` automatically
|
|
796
|
-
- Multiple recipients supported in the `to` array for email
|
|
797
|
-
- Alerts are sent asynchronously (won't block backup completion)
|
|
798
|
-
|
|
799
|
-
## Requirements
|
|
800
|
-
|
|
801
|
-
- Node.js 18 or higher
|
|
802
|
-
- PostgreSQL 12+
|
|
803
|
-
- PostgreSQL client tools (`pg_dump`, `pg_restore`, `psql`)
|
|
804
|
-
|
|
805
|
-
**Installing PostgreSQL client tools:**
|
|
806
|
-
|
|
807
|
-
```bash
|
|
808
|
-
# macOS
|
|
809
|
-
brew install postgresql
|
|
735
|
+
## Troubleshooting
|
|
810
736
|
|
|
811
|
-
|
|
812
|
-
sudo apt-get install postgresql-client
|
|
737
|
+
**First step, always:**
|
|
813
738
|
|
|
814
|
-
|
|
815
|
-
|
|
739
|
+
```bash
|
|
740
|
+
npx dbdock test
|
|
816
741
|
```
|
|
817
742
|
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
Run `npx dbdock test` to verify your configuration.
|
|
743
|
+
This tests your database, storage, and alert config in one go.
|
|
821
744
|
|
|
822
|
-
###
|
|
745
|
+
### pg_dump / pg_restore / psql not found
|
|
823
746
|
|
|
824
|
-
|
|
747
|
+
You need PostgreSQL client tools installed:
|
|
825
748
|
|
|
826
749
|
```bash
|
|
827
|
-
# macOS
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
# Ubuntu/Debian
|
|
831
|
-
sudo apt-get install postgresql-client
|
|
750
|
+
brew install postgresql # macOS
|
|
751
|
+
sudo apt-get install postgresql-client # Ubuntu/Debian
|
|
832
752
|
```
|
|
833
753
|
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
- Verify `host`, `port`, `username`, `password`, `database` in config
|
|
837
|
-
- Test connection: `psql -h HOST -p PORT -U USERNAME -d DATABASE`
|
|
838
|
-
- Check PostgreSQL server is running
|
|
839
|
-
- Verify network/firewall allows connection
|
|
754
|
+
### Can't connect to database
|
|
840
755
|
|
|
841
|
-
|
|
756
|
+
- Double-check `host`, `port`, `username`, `password`, `database` in config
|
|
757
|
+
- Test manually: `psql -h HOST -p PORT -U USERNAME -d DATABASE`
|
|
758
|
+
- Make sure the PostgreSQL server is actually running
|
|
759
|
+
- Check firewalls / security groups if it's a remote database
|
|
842
760
|
|
|
843
|
-
|
|
761
|
+
### Storage errors
|
|
844
762
|
|
|
845
|
-
|
|
846
|
-
- Ensure IAM user has permissions: `s3:PutObject`, `s3:GetObject`, `s3:ListBucket`, `s3:DeleteObject`
|
|
847
|
-
- Check bucket name and region
|
|
763
|
+
**S3:** Check credentials, bucket name, region. IAM user needs `s3:PutObject`, `s3:GetObject`, `s3:ListBucket`, `s3:DeleteObject`.
|
|
848
764
|
|
|
849
|
-
|
|
765
|
+
**R2:** Check endpoint format (`https://ACCOUNT_ID.r2.cloudflarestorage.com`), verify API token and bucket exist.
|
|
850
766
|
|
|
851
|
-
|
|
852
|
-
- Check endpoint URL format: `https://ACCOUNT_ID.r2.cloudflarestorage.com`
|
|
853
|
-
- Ensure bucket exists and is accessible
|
|
854
|
-
- Verify R2 credentials have read/write permissions
|
|
767
|
+
**Cloudinary:** Verify cloud name, API key, API secret. Make sure the account is active.
|
|
855
768
|
|
|
856
|
-
|
|
769
|
+
### Encryption key issues
|
|
857
770
|
|
|
858
|
-
|
|
859
|
-
- Check your Cloudinary account is active
|
|
860
|
-
- Ensure API credentials have media library access
|
|
861
|
-
|
|
862
|
-
**Encryption key errors:**
|
|
771
|
+
Key must be exactly 64 hex characters. Generate a valid one:
|
|
863
772
|
|
|
864
773
|
```bash
|
|
865
|
-
# Generate a valid 64-character hex key
|
|
866
774
|
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
|
|
867
|
-
|
|
868
|
-
# Must be exactly 64 hexadecimal characters (0-9, a-f, A-F)
|
|
869
775
|
```
|
|
870
776
|
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
- Ensure backups are in `dbdock_backups/` folder
|
|
874
|
-
- Verify backup files are named with `.sql` extension
|
|
875
|
-
- Check endpoint configuration matches R2 account ID
|
|
876
|
-
|
|
877
|
-
**No backups found:**
|
|
878
|
-
|
|
879
|
-
- Local: Check files exist in configured path
|
|
880
|
-
- S3/R2: Verify files are in `dbdock_backups/` folder
|
|
881
|
-
- Cloudinary: Check Media Library for `dbdock_backups` folder
|
|
882
|
-
- Ensure files match pattern: `backup-*.sql`
|
|
883
|
-
|
|
884
|
-
DBDock shows clear, actionable error messages for all issues with specific troubleshooting steps.
|
|
885
|
-
|
|
886
|
-
## Documentation
|
|
887
|
-
|
|
888
|
-
📚 **[Full Documentation](https://dbdock.mintlify.app)** - Comprehensive guides, API reference, and examples
|
|
777
|
+
### No backups found during restore
|
|
889
778
|
|
|
890
|
-
|
|
779
|
+
- **Local:** Check the configured path has files
|
|
780
|
+
- **S3/R2:** Files should be in `dbdock_backups/` folder
|
|
781
|
+
- **Cloudinary:** Check Media Library for `dbdock_backups` folder
|
|
782
|
+
- Files should match: `backup-*.sql`
|
|
891
783
|
|
|
892
|
-
|
|
893
|
-
- 🐛 **[Issues](https://github.com/naheemolaide/dbdock-support/issues)** - Report bugs and request features
|
|
784
|
+
---
|
|
894
785
|
|
|
895
786
|
## Links
|
|
896
787
|
|
|
897
|
-
-
|
|
898
|
-
-
|
|
899
|
-
-
|
|
788
|
+
- [npm Package](https://www.npmjs.com/package/dbdock)
|
|
789
|
+
- [Full Documentation](https://dbdock.mintlify.app)
|
|
790
|
+
- [GitHub](https://github.com/naheemolaide/dbdock-support)
|
|
791
|
+
- [Discussions](https://github.com/naheemolaide/dbdock-support/discussions)
|
|
792
|
+
- [Report Issues](https://github.com/naheemolaide/dbdock-support/issues)
|
|
900
793
|
|
|
901
794
|
## License
|
|
902
795
|
|