spindb 0.19.5 → 0.21.3
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 +83 -35
- package/cli/commands/backup.ts +63 -66
- package/cli/commands/clone.ts +33 -8
- package/cli/commands/create.ts +222 -110
- package/cli/commands/databases.ts +279 -0
- package/cli/commands/delete.ts +23 -8
- package/cli/commands/engines.ts +78 -3
- package/cli/commands/info.ts +16 -4
- package/cli/commands/list.ts +5 -1
- package/cli/commands/menu/backup-handlers.ts +185 -153
- package/cli/commands/menu/container-handlers.ts +25 -19
- package/cli/commands/menu/engine-handlers.ts +110 -156
- package/cli/commands/menu/index.ts +2 -2
- package/cli/commands/menu/shell-handlers.ts +246 -6
- package/cli/commands/menu/sql-handlers.ts +16 -10
- package/cli/commands/restore.ts +136 -52
- package/cli/commands/start.ts +35 -13
- package/cli/commands/stop.ts +41 -14
- package/cli/constants.ts +1 -0
- package/cli/helpers.ts +71 -0
- package/cli/index.ts +11 -25
- package/cli/ui/prompts.ts +34 -37
- package/config/backup-formats.ts +218 -133
- package/config/engine-defaults.ts +13 -0
- package/config/engines.json +17 -1
- package/config/engines.schema.json +1 -1
- package/config/paths.ts +99 -2
- package/core/backup-restore.ts +2 -2
- package/core/base-binary-manager.ts +551 -0
- package/core/base-document-binary-manager.ts +504 -0
- package/core/base-embedded-binary-manager.ts +538 -0
- package/core/base-server-binary-manager.ts +507 -0
- package/core/config-manager.ts +5 -0
- package/core/container-manager.ts +82 -20
- package/core/dependency-manager.ts +2 -0
- package/core/fs-error-utils.ts +79 -0
- package/core/hostdb-client.ts +34 -4
- package/core/hostdb-releases-factory.ts +237 -0
- package/core/platform-service.ts +20 -3
- package/core/start-with-retry.ts +1 -11
- package/core/version-utils.ts +27 -0
- package/engines/clickhouse/backup.ts +15 -1
- package/engines/clickhouse/binary-manager.ts +59 -292
- package/engines/clickhouse/binary-urls.ts +7 -5
- package/engines/clickhouse/hostdb-releases.ts +13 -101
- package/engines/clickhouse/index.ts +247 -8
- package/engines/clickhouse/restore.ts +54 -11
- package/engines/clickhouse/version-maps.ts +2 -0
- package/engines/duckdb/binary-manager.ts +20 -461
- package/engines/duckdb/hostdb-releases.ts +13 -112
- package/engines/duckdb/index.ts +93 -33
- package/engines/index.ts +4 -0
- package/engines/mariadb/binary-manager.ts +22 -377
- package/engines/mariadb/binary-urls.ts +12 -6
- package/engines/mariadb/hostdb-releases.ts +12 -93
- package/engines/mongodb/backup.ts +9 -8
- package/engines/mongodb/binary-manager.ts +24 -415
- package/engines/mongodb/binary-urls.ts +10 -10
- package/engines/mongodb/restore.ts +40 -9
- package/engines/mysql/binary-manager.ts +19 -375
- package/engines/mysql/binary-urls.ts +12 -6
- package/engines/mysql/hostdb-releases.ts +27 -104
- package/engines/postgresql/binary-manager.ts +90 -576
- package/engines/postgresql/binary-urls.ts +13 -5
- package/engines/postgresql/hostdb-releases.ts +12 -84
- package/engines/postgresql/index.ts +175 -78
- package/engines/postgresql/restore.ts +24 -18
- package/engines/qdrant/api-client.ts +61 -0
- package/engines/qdrant/backup.ts +165 -0
- package/engines/qdrant/binary-manager.ts +43 -0
- package/engines/qdrant/binary-urls.ts +115 -0
- package/engines/qdrant/cli-utils.ts +45 -0
- package/engines/qdrant/hostdb-releases.ts +23 -0
- package/engines/qdrant/index.ts +1112 -0
- package/engines/qdrant/restore.ts +203 -0
- package/engines/qdrant/version-maps.ts +78 -0
- package/engines/qdrant/version-validator.ts +128 -0
- package/engines/redis/backup.ts +5 -5
- package/engines/redis/binary-manager.ts +21 -452
- package/engines/redis/binary-urls.ts +12 -6
- package/engines/redis/hostdb-releases.ts +12 -83
- package/engines/redis/index.ts +365 -10
- package/engines/redis/restore.ts +51 -21
- package/engines/sqlite/binary-manager.ts +26 -466
- package/engines/sqlite/hostdb-releases.ts +13 -110
- package/engines/sqlite/index.ts +46 -17
- package/engines/valkey/backup.ts +5 -5
- package/engines/valkey/binary-manager.ts +22 -454
- package/engines/valkey/binary-urls.ts +7 -6
- package/engines/valkey/hostdb-releases.ts +12 -91
- package/engines/valkey/index.ts +384 -10
- package/engines/valkey/restore.ts +51 -22
- package/package.json +6 -3
- package/types/index.ts +49 -1
- package/core/binary-manager.ts +0 -801
- package/engines/postgresql/edb-binary-urls.ts +0 -158
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
**One CLI for all your local databases.**
|
|
9
9
|
|
|
10
|
-
SpinDB is a universal database management tool that combines a package manager, a unified API, and native client tooling for
|
|
10
|
+
SpinDB is a universal database management tool that combines a package manager, a unified API, and native client tooling for 10 different database engines—all from a single command-line interface. No Docker, no VMs, no platform-specific installers. Just databases, running natively on your machine.
|
|
11
11
|
|
|
12
12
|
```bash
|
|
13
13
|
npm install -g spindb
|
|
@@ -48,7 +48,7 @@ One consistent interface across SQL databases, document stores, key-value stores
|
|
|
48
48
|
|
|
49
49
|
```bash
|
|
50
50
|
# Same commands work for ANY database
|
|
51
|
-
spindb create mydb --engine [postgresql|mysql|mariadb|mongodb|redis|valkey|clickhouse|sqlite|duckdb]
|
|
51
|
+
spindb create mydb --engine [postgresql|mysql|mariadb|mongodb|redis|valkey|clickhouse|sqlite|duckdb|qdrant]
|
|
52
52
|
spindb start mydb
|
|
53
53
|
spindb connect mydb
|
|
54
54
|
spindb backup mydb
|
|
@@ -70,7 +70,7 @@ spindb run mydb -c "SELECT * FROM system.tables" # ClickHouse
|
|
|
70
70
|
|
|
71
71
|
## Platform Coverage
|
|
72
72
|
|
|
73
|
-
SpinDB works across **
|
|
73
|
+
SpinDB works across **10 database engines** and **5 platform architectures** with a **single, consistent API**.
|
|
74
74
|
|
|
75
75
|
| Database | macOS ARM64 | macOS Intel | Linux x64 | Linux ARM64 | Windows x64 |
|
|
76
76
|
|----------|:-----------:|:-----------:|:---------:|:-----------:|:-----------:|
|
|
@@ -83,8 +83,9 @@ SpinDB works across **9 database engines** and **5 platform architectures** with
|
|
|
83
83
|
| 🔴 **Redis** | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
84
84
|
| 🔷 **Valkey** | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
85
85
|
| 🏠 **ClickHouse** | ✅ | ✅ | ✅ | ✅ | ❌ |
|
|
86
|
+
| 🧭 **Qdrant** | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
86
87
|
|
|
87
|
-
**
|
|
88
|
+
**49 combinations. One CLI. Zero configuration.**
|
|
88
89
|
|
|
89
90
|
---
|
|
90
91
|
|
|
@@ -163,7 +164,7 @@ SpinDB runs databases as **native processes** with **isolated data directories**
|
|
|
163
164
|
| Feature | SpinDB | Docker | DBngin | Postgres.app | XAMPP |
|
|
164
165
|
|---------|--------|--------|--------|--------------|-------|
|
|
165
166
|
| No Docker required | ✅ | ❌ | ✅ | ✅ | ✅ |
|
|
166
|
-
| Multiple DB engines | ✅
|
|
167
|
+
| Multiple DB engines | ✅ 10 engines | ✅ Unlimited | ✅ 3 engines | ❌ PostgreSQL only | ⚠️ MySQL only |
|
|
167
168
|
| CLI-first | ✅ | ✅ | ❌ GUI-first | ❌ GUI-first | ❌ GUI-first |
|
|
168
169
|
| Multiple versions | ✅ | ✅ | ✅ | ✅ | ❌ |
|
|
169
170
|
| Clone databases | ✅ | Manual | ✅ | ❌ | ❌ |
|
|
@@ -177,7 +178,7 @@ SpinDB runs databases as **native processes** with **isolated data directories**
|
|
|
177
178
|
|
|
178
179
|
## Supported Databases
|
|
179
180
|
|
|
180
|
-
SpinDB supports **
|
|
181
|
+
SpinDB supports **10 database engines** with **multiple versions** for each:
|
|
181
182
|
|
|
182
183
|
| Engine | Type | Versions | Default Port | Query Language |
|
|
183
184
|
|--------|------|----------|--------------|----------------|
|
|
@@ -190,10 +191,11 @@ SpinDB supports **9 database engines** with **multiple versions** for each:
|
|
|
190
191
|
| 🔴 **Redis** | Key-Value Store | 7, 8 | 6379 | Redis commands |
|
|
191
192
|
| 🔷 **Valkey** | Key-Value Store | 8, 9 | 6379 | Redis commands |
|
|
192
193
|
| 🏠 **ClickHouse** | Columnar OLAP | 25.12 | 9000 (TCP), 8123 (HTTP) | SQL (ClickHouse dialect) |
|
|
194
|
+
| 🧭 **Qdrant** | Vector Search | 1 | 6333 (HTTP), 6334 (gRPC) | REST API |
|
|
193
195
|
|
|
194
196
|
### Engine Categories
|
|
195
197
|
|
|
196
|
-
**Server-Based Databases** (PostgreSQL, MySQL, MariaDB, MongoDB, Redis, Valkey, ClickHouse):
|
|
198
|
+
**Server-Based Databases** (PostgreSQL, MySQL, MariaDB, MongoDB, Redis, Valkey, ClickHouse, Qdrant):
|
|
197
199
|
- Start/stop server processes
|
|
198
200
|
- Bind to localhost ports
|
|
199
201
|
- Data stored in `~/.spindb/containers/{engine}/{name}/`
|
|
@@ -269,8 +271,8 @@ psql $(spindb url mydb)
|
|
|
269
271
|
spindb backup mydb # Auto-generated filename
|
|
270
272
|
spindb backup mydb --name production-backup # Custom name
|
|
271
273
|
spindb backup mydb --output ./backups/ # Custom directory
|
|
272
|
-
spindb backup mydb --format sql # SQL text format
|
|
273
|
-
spindb backup mydb --format
|
|
274
|
+
spindb backup mydb --format sql # SQL text format (PostgreSQL)
|
|
275
|
+
spindb backup mydb --format custom # Custom binary format (PostgreSQL)
|
|
274
276
|
|
|
275
277
|
# Restore from backups
|
|
276
278
|
spindb restore mydb backup.dump
|
|
@@ -307,6 +309,12 @@ spindb edit mydb --relocate ~/new/path # Move SQLite/DuckDB file
|
|
|
307
309
|
spindb logs mydb
|
|
308
310
|
spindb logs mydb --follow # Follow mode (tail -f)
|
|
309
311
|
spindb logs mydb -n 100 # Last 100 lines
|
|
312
|
+
|
|
313
|
+
# Manage database tracking (for external scripts)
|
|
314
|
+
spindb databases list mydb # List tracked databases
|
|
315
|
+
spindb databases add mydb analytics # Add to tracking
|
|
316
|
+
spindb databases remove mydb old_backup # Remove from tracking
|
|
317
|
+
spindb databases sync mydb oldname newname # Sync after rename
|
|
310
318
|
```
|
|
311
319
|
|
|
312
320
|
### Engine & System Management
|
|
@@ -430,7 +438,7 @@ spindb create modern --engine postgresql --db-version 18
|
|
|
430
438
|
|
|
431
439
|
# Backup formats
|
|
432
440
|
spindb backup myapp --format sql # Plain SQL (.sql)
|
|
433
|
-
spindb backup myapp --format
|
|
441
|
+
spindb backup myapp --format custom # Binary custom format (.dump)
|
|
434
442
|
```
|
|
435
443
|
|
|
436
444
|
**Versions:** 15, 16, 17, 18
|
|
@@ -527,6 +535,23 @@ spindb run warehouse -c "SELECT * FROM system.tables"
|
|
|
527
535
|
**Ports:** 9000 (native TCP), 8123 (HTTP)
|
|
528
536
|
**Tools:** `clickhouse-client`, `clickhouse-server` (included)
|
|
529
537
|
|
|
538
|
+
### Qdrant 🧭
|
|
539
|
+
|
|
540
|
+
```bash
|
|
541
|
+
# Create Qdrant database (vector similarity search)
|
|
542
|
+
spindb create vectors --engine qdrant
|
|
543
|
+
spindb start vectors
|
|
544
|
+
|
|
545
|
+
# Access via REST API
|
|
546
|
+
curl http://127.0.0.1:6333/collections
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
**Version:** 1 (1.16.3)
|
|
550
|
+
**Platforms:** macOS, Linux, Windows (all platforms)
|
|
551
|
+
**Ports:** 6333 (REST/HTTP), 6334 (gRPC)
|
|
552
|
+
**Query interface:** REST API (no CLI shell - use curl or API clients)
|
|
553
|
+
**Tools:** `qdrant` (included)
|
|
554
|
+
|
|
530
555
|
---
|
|
531
556
|
|
|
532
557
|
## Enhanced CLI Tools
|
|
@@ -544,6 +569,7 @@ SpinDB supports enhanced database shells with auto-completion, syntax highlighti
|
|
|
544
569
|
| Redis | `redis-cli` | `iredis` | - |
|
|
545
570
|
| Valkey | `valkey-cli` | `iredis` (compatible) | - |
|
|
546
571
|
| ClickHouse | `clickhouse-client` | - | `usql` |
|
|
572
|
+
| Qdrant | REST API | - | - |
|
|
547
573
|
|
|
548
574
|
Install and use in one command:
|
|
549
575
|
|
|
@@ -563,12 +589,12 @@ Every engine supports backup and restore with engine-specific formats:
|
|
|
563
589
|
|
|
564
590
|
| Format | Extension | Tool | Use Case |
|
|
565
591
|
|--------|-----------|------|----------|
|
|
566
|
-
|
|
|
567
|
-
|
|
|
592
|
+
| sql | `.sql` | pg_dump | Human-readable, portable |
|
|
593
|
+
| custom | `.dump` | pg_dump -Fc | Compressed, faster restore |
|
|
568
594
|
|
|
569
595
|
```bash
|
|
570
|
-
spindb backup mydb --sql
|
|
571
|
-
spindb backup mydb --
|
|
596
|
+
spindb backup mydb --format sql # Plain SQL
|
|
597
|
+
spindb backup mydb --format custom # Binary custom format
|
|
572
598
|
spindb restore mydb backup.dump
|
|
573
599
|
```
|
|
574
600
|
|
|
@@ -576,23 +602,23 @@ spindb restore mydb backup.dump
|
|
|
576
602
|
|
|
577
603
|
| Format | Extension | Tool | Use Case |
|
|
578
604
|
|--------|-----------|------|----------|
|
|
579
|
-
|
|
|
580
|
-
|
|
|
605
|
+
| sql | `.sql` | mysqldump / mariadb-dump | Human-readable |
|
|
606
|
+
| compressed | `.sql.gz` | mysqldump + gzip | Smaller file size |
|
|
581
607
|
|
|
582
608
|
```bash
|
|
583
|
-
spindb backup mydb --sql
|
|
584
|
-
spindb backup mydb --
|
|
609
|
+
spindb backup mydb --format sql # Plain SQL
|
|
610
|
+
spindb backup mydb --format compressed # Compressed SQL
|
|
585
611
|
```
|
|
586
612
|
|
|
587
613
|
### MongoDB
|
|
588
614
|
|
|
589
615
|
| Format | Extension | Tool | Use Case |
|
|
590
616
|
|--------|-----------|------|----------|
|
|
591
|
-
|
|
|
592
|
-
|
|
|
617
|
+
| bson | _(directory)_ | mongodump | Binary, preserves all types |
|
|
618
|
+
| archive | `.archive` | mongodump --archive | Single compressed file |
|
|
593
619
|
|
|
594
620
|
```bash
|
|
595
|
-
spindb backup mydb
|
|
621
|
+
spindb backup mydb --format bson # BSON directory
|
|
596
622
|
spindb backup mydb --format archive # Single .archive file
|
|
597
623
|
```
|
|
598
624
|
|
|
@@ -600,12 +626,12 @@ spindb backup mydb --format archive # Single .archive file
|
|
|
600
626
|
|
|
601
627
|
| Format | Extension | Tool | Use Case |
|
|
602
628
|
|--------|-----------|------|----------|
|
|
603
|
-
|
|
|
604
|
-
|
|
|
629
|
+
| rdb | `.rdb` | BGSAVE | Binary snapshot, requires stop/start |
|
|
630
|
+
| text | `.redis` / `.valkey` | Custom | Human-readable commands |
|
|
605
631
|
|
|
606
632
|
```bash
|
|
607
|
-
spindb backup mydb --
|
|
608
|
-
spindb backup mydb --
|
|
633
|
+
spindb backup mydb --format rdb # RDB snapshot (default)
|
|
634
|
+
spindb backup mydb --format text # Text commands
|
|
609
635
|
|
|
610
636
|
# Restore with merge or replace strategy
|
|
611
637
|
spindb restore mydb backup.redis # Prompts: Replace all / Merge
|
|
@@ -615,23 +641,32 @@ spindb restore mydb backup.redis # Prompts: Replace all / Merge
|
|
|
615
641
|
|
|
616
642
|
| Format | Extension | Tool | Use Case |
|
|
617
643
|
|--------|-----------|------|----------|
|
|
618
|
-
|
|
|
619
|
-
|
|
|
644
|
+
| sql | `.sql` | .dump / duckdb | Human-readable |
|
|
645
|
+
| binary | `.sqlite` / `.duckdb` | File copy | Exact database copy |
|
|
620
646
|
|
|
621
647
|
```bash
|
|
622
|
-
spindb backup mydb --sql
|
|
623
|
-
spindb backup mydb --
|
|
648
|
+
spindb backup mydb --format sql # SQL dump
|
|
649
|
+
spindb backup mydb --format binary # Binary copy (default)
|
|
624
650
|
```
|
|
625
651
|
|
|
626
652
|
### ClickHouse
|
|
627
653
|
|
|
628
654
|
| Format | Extension | Tool | Use Case |
|
|
629
655
|
|--------|-----------|------|----------|
|
|
630
|
-
|
|
|
631
|
-
|
|
656
|
+
| sql | `.sql` | clickhouse-client | Plain SQL dump |
|
|
657
|
+
|
|
658
|
+
```bash
|
|
659
|
+
spindb backup mydb --format sql # SQL dump (only format)
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
### Qdrant
|
|
663
|
+
|
|
664
|
+
| Format | Extension | Tool | Use Case |
|
|
665
|
+
|--------|-----------|------|----------|
|
|
666
|
+
| snapshot | `.snapshot` | REST API | Full database snapshot |
|
|
632
667
|
|
|
633
668
|
```bash
|
|
634
|
-
spindb backup mydb --
|
|
669
|
+
spindb backup mydb --format snapshot # Snapshot (only format)
|
|
635
670
|
```
|
|
636
671
|
|
|
637
672
|
---
|
|
@@ -652,7 +687,7 @@ spindb start staging
|
|
|
652
687
|
|
|
653
688
|
### Restore from Remote
|
|
654
689
|
|
|
655
|
-
Pull production data into local databases
|
|
690
|
+
Pull production data into local databases. **All engines support remote restore via connection strings:**
|
|
656
691
|
|
|
657
692
|
```bash
|
|
658
693
|
# Create new database from remote
|
|
@@ -662,6 +697,19 @@ spindb create prod-copy --from "postgresql://user:pass@prod-host:5432/production
|
|
|
662
697
|
spindb restore mydb --from-url "postgresql://user:pass@prod-host:5432/production"
|
|
663
698
|
```
|
|
664
699
|
|
|
700
|
+
**Supported connection string formats:**
|
|
701
|
+
|
|
702
|
+
| Engine | Format | Example |
|
|
703
|
+
|--------|--------|---------|
|
|
704
|
+
| PostgreSQL | `postgresql://` or `postgres://` | `postgresql://user:pass@host:5432/db` |
|
|
705
|
+
| MySQL | `mysql://` | `mysql://root:pass@host:3306/db` |
|
|
706
|
+
| MariaDB | `mysql://` or `mariadb://` | `mariadb://root:pass@host:3307/db` |
|
|
707
|
+
| MongoDB | `mongodb://` or `mongodb+srv://` | `mongodb://user:pass@host:27017/db` |
|
|
708
|
+
| Redis | `redis://` | `redis://:password@host:6379/0` |
|
|
709
|
+
| Valkey | `redis://` | `redis://:password@host:6379/0` |
|
|
710
|
+
| ClickHouse | `clickhouse://` or `http://` | `clickhouse://default:pass@host:8123/db` |
|
|
711
|
+
| Qdrant | `qdrant://` or `http://` | `http://host:6333?api_key=KEY` |
|
|
712
|
+
|
|
665
713
|
### Multi-Version Support
|
|
666
714
|
|
|
667
715
|
Run different versions of the same database simultaneously:
|
|
@@ -742,7 +790,7 @@ The following engines may be added based on community interest:
|
|
|
742
790
|
|
|
743
791
|
- **Local only** - Databases bind to `127.0.0.1`. Remote connection support planned for v1.1.
|
|
744
792
|
- **ClickHouse Windows** - Not supported (hostdb doesn't build for Windows).
|
|
745
|
-
- **
|
|
793
|
+
- **Qdrant** - Uses REST API instead of CLI shell. Access via HTTP at the configured port.
|
|
746
794
|
|
|
747
795
|
---
|
|
748
796
|
|
|
@@ -817,7 +865,7 @@ See [FEATURE.md](FEATURE.md) for adding new database engines.
|
|
|
817
865
|
|
|
818
866
|
SpinDB is powered by:
|
|
819
867
|
|
|
820
|
-
- **[hostdb](https://github.com/robertjbass/hostdb)** - Pre-compiled database binaries for
|
|
868
|
+
- **[hostdb](https://github.com/robertjbass/hostdb)** - Pre-compiled database binaries for 10 engines across all major platforms. Makes Docker-free multi-version database support possible.
|
|
821
869
|
|
|
822
870
|
---
|
|
823
871
|
|
package/cli/commands/backup.ts
CHANGED
|
@@ -14,6 +14,15 @@ import {
|
|
|
14
14
|
import { createSpinner } from '../ui/spinner'
|
|
15
15
|
import { uiSuccess, uiError, uiWarning, formatBytes } from '../ui/theme'
|
|
16
16
|
import { getMissingDependencies } from '../../core/dependency-manager'
|
|
17
|
+
import { isFileBasedEngine } from '../../types'
|
|
18
|
+
import {
|
|
19
|
+
getBackupExtension,
|
|
20
|
+
getBackupSpinnerLabel,
|
|
21
|
+
getDefaultFormat,
|
|
22
|
+
isValidFormat,
|
|
23
|
+
getValidFormats,
|
|
24
|
+
} from '../../config/backup-formats'
|
|
25
|
+
import type { BackupFormatType } from '../../types'
|
|
17
26
|
|
|
18
27
|
function generateTimestamp(): string {
|
|
19
28
|
const now = new Date()
|
|
@@ -28,42 +37,6 @@ function generateDefaultFilename(
|
|
|
28
37
|
return `${containerName}-${database}-backup-${timestamp}`
|
|
29
38
|
}
|
|
30
39
|
|
|
31
|
-
function getExtension(format: 'sql' | 'dump', engine: string): string {
|
|
32
|
-
// Handle 'sql' format (human-readable option)
|
|
33
|
-
if (format === 'sql') {
|
|
34
|
-
// MongoDB uses BSON directory format for 'sql' choice
|
|
35
|
-
return engine === 'mongodb' ? '' : '.sql'
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Handle 'dump' format (binary/compressed option)
|
|
39
|
-
switch (engine) {
|
|
40
|
-
case 'mysql':
|
|
41
|
-
return '.sql.gz'
|
|
42
|
-
case 'sqlite':
|
|
43
|
-
return '.sqlite'
|
|
44
|
-
case 'mongodb':
|
|
45
|
-
return '.archive'
|
|
46
|
-
case 'redis':
|
|
47
|
-
return '.rdb'
|
|
48
|
-
case 'postgresql':
|
|
49
|
-
default:
|
|
50
|
-
return '.dump'
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function getFormatDescription(format: 'sql' | 'dump', engine: string): string {
|
|
55
|
-
if (engine === 'redis') {
|
|
56
|
-
return 'RDB snapshot'
|
|
57
|
-
}
|
|
58
|
-
if (engine === 'mongodb') {
|
|
59
|
-
return format === 'sql' ? 'BSON directory' : 'archive'
|
|
60
|
-
}
|
|
61
|
-
if (engine === 'sqlite') {
|
|
62
|
-
return format === 'sql' ? 'SQL' : 'binary'
|
|
63
|
-
}
|
|
64
|
-
return format === 'sql' ? 'SQL' : 'dump'
|
|
65
|
-
}
|
|
66
|
-
|
|
67
40
|
export const backupCommand = new Command('backup')
|
|
68
41
|
.description('Create a backup of a database')
|
|
69
42
|
.argument('[container]', 'Container name')
|
|
@@ -73,9 +46,7 @@ export const backupCommand = new Command('backup')
|
|
|
73
46
|
'-o, --output <path>',
|
|
74
47
|
'Output directory (defaults to current directory)',
|
|
75
48
|
)
|
|
76
|
-
.option('--format <format>', '
|
|
77
|
-
.option('--sql', 'Output as plain SQL (shorthand for --format sql)')
|
|
78
|
-
.option('--dump', 'Output as dump format (shorthand for --format dump)')
|
|
49
|
+
.option('--format <format>', 'Backup format (engine-specific, e.g., sql, custom, rdb, binary)')
|
|
79
50
|
.option('-j, --json', 'Output result as JSON')
|
|
80
51
|
.action(
|
|
81
52
|
async (
|
|
@@ -85,8 +56,6 @@ export const backupCommand = new Command('backup')
|
|
|
85
56
|
name?: string
|
|
86
57
|
output?: string
|
|
87
58
|
format?: string
|
|
88
|
-
sql?: boolean
|
|
89
|
-
dump?: boolean
|
|
90
59
|
json?: boolean
|
|
91
60
|
},
|
|
92
61
|
) => {
|
|
@@ -94,6 +63,12 @@ export const backupCommand = new Command('backup')
|
|
|
94
63
|
let containerName = containerArg
|
|
95
64
|
|
|
96
65
|
if (!containerName) {
|
|
66
|
+
// JSON mode requires container name argument
|
|
67
|
+
if (options.json) {
|
|
68
|
+
console.log(JSON.stringify({ error: 'Container name is required' }))
|
|
69
|
+
process.exit(1)
|
|
70
|
+
}
|
|
71
|
+
|
|
97
72
|
const containers = await containerManager.list()
|
|
98
73
|
const running = containers.filter((c) => c.status === 'running')
|
|
99
74
|
|
|
@@ -124,22 +99,30 @@ export const backupCommand = new Command('backup')
|
|
|
124
99
|
|
|
125
100
|
const config = await containerManager.getConfig(containerName)
|
|
126
101
|
if (!config) {
|
|
127
|
-
|
|
102
|
+
if (options.json) {
|
|
103
|
+
console.log(JSON.stringify({ error: `Container "${containerName}" not found` }))
|
|
104
|
+
} else {
|
|
105
|
+
console.error(uiError(`Container "${containerName}" not found`))
|
|
106
|
+
}
|
|
128
107
|
process.exit(1)
|
|
129
108
|
}
|
|
130
109
|
|
|
131
110
|
const { engine: engineName } = config
|
|
132
111
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
-
|
|
112
|
+
// File-based engines (SQLite, DuckDB) don't need to be "running"
|
|
113
|
+
if (!isFileBasedEngine(engineName)) {
|
|
114
|
+
const running = await processManager.isRunning(containerName, {
|
|
115
|
+
engine: engineName,
|
|
116
|
+
})
|
|
117
|
+
if (!running) {
|
|
118
|
+
const errorMsg = `Container "${containerName}" is not running. Start it first.`
|
|
119
|
+
if (options.json) {
|
|
120
|
+
console.log(JSON.stringify({ error: errorMsg }))
|
|
121
|
+
} else {
|
|
122
|
+
console.error(uiError(errorMsg))
|
|
123
|
+
}
|
|
124
|
+
process.exit(1)
|
|
125
|
+
}
|
|
143
126
|
}
|
|
144
127
|
|
|
145
128
|
const engine = getEngine(engineName)
|
|
@@ -193,20 +176,26 @@ export const backupCommand = new Command('backup')
|
|
|
193
176
|
}
|
|
194
177
|
}
|
|
195
178
|
|
|
196
|
-
let format:
|
|
179
|
+
let format: BackupFormatType = getDefaultFormat(engineName)
|
|
197
180
|
|
|
198
|
-
if (options.
|
|
199
|
-
format
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
181
|
+
if (options.format) {
|
|
182
|
+
if (!isValidFormat(engineName, options.format)) {
|
|
183
|
+
const validFormats = getValidFormats(engineName)
|
|
184
|
+
const errorMsg = `Invalid format "${options.format}" for ${engineName}. Valid formats: ${validFormats.join(', ')}`
|
|
185
|
+
if (options.json) {
|
|
186
|
+
console.log(JSON.stringify({ error: errorMsg }))
|
|
187
|
+
} else {
|
|
188
|
+
console.error(uiError(errorMsg))
|
|
189
|
+
}
|
|
205
190
|
process.exit(1)
|
|
206
191
|
}
|
|
207
|
-
|
|
192
|
+
// Safe to cast: isValidFormat above guarantees the format is valid
|
|
193
|
+
format = options.format as BackupFormatType
|
|
208
194
|
} else if (!containerArg) {
|
|
209
|
-
|
|
195
|
+
const selectedFormat = await promptBackupFormat(engineName)
|
|
196
|
+
if (selectedFormat) {
|
|
197
|
+
format = selectedFormat
|
|
198
|
+
}
|
|
210
199
|
}
|
|
211
200
|
|
|
212
201
|
const defaultFilename = generateDefaultFilename(
|
|
@@ -219,13 +208,13 @@ export const backupCommand = new Command('backup')
|
|
|
219
208
|
filename = await promptBackupFilename(defaultFilename)
|
|
220
209
|
}
|
|
221
210
|
|
|
222
|
-
const extension =
|
|
211
|
+
const extension = getBackupExtension(engineName, format)
|
|
223
212
|
const outputDir = options.output || process.cwd()
|
|
224
213
|
const outputPath = join(outputDir, `${filename}${extension}`)
|
|
225
214
|
|
|
226
|
-
const
|
|
215
|
+
const spinnerLabel = getBackupSpinnerLabel(engineName, format)
|
|
227
216
|
const backupSpinner = createSpinner(
|
|
228
|
-
`Creating ${
|
|
217
|
+
`Creating ${spinnerLabel} backup of "${databaseName}"...`,
|
|
229
218
|
)
|
|
230
219
|
backupSpinner.start()
|
|
231
220
|
|
|
@@ -269,6 +258,10 @@ export const backupCommand = new Command('backup')
|
|
|
269
258
|
)
|
|
270
259
|
|
|
271
260
|
if (matchingPattern) {
|
|
261
|
+
if (options.json) {
|
|
262
|
+
console.log(JSON.stringify({ error: e.message }))
|
|
263
|
+
process.exit(1)
|
|
264
|
+
}
|
|
272
265
|
const missingTool = matchingPattern.replace(' not found', '')
|
|
273
266
|
const installed = await promptInstallDependencies(missingTool)
|
|
274
267
|
if (installed) {
|
|
@@ -279,7 +272,11 @@ export const backupCommand = new Command('backup')
|
|
|
279
272
|
process.exit(1)
|
|
280
273
|
}
|
|
281
274
|
|
|
282
|
-
|
|
275
|
+
if (options.json) {
|
|
276
|
+
console.log(JSON.stringify({ error: e.message }))
|
|
277
|
+
} else {
|
|
278
|
+
console.error(uiError(e.message))
|
|
279
|
+
}
|
|
283
280
|
process.exit(1)
|
|
284
281
|
}
|
|
285
282
|
},
|
package/cli/commands/clone.ts
CHANGED
|
@@ -23,6 +23,12 @@ export const cloneCommand = new Command('clone')
|
|
|
23
23
|
let targetName = target
|
|
24
24
|
|
|
25
25
|
if (!sourceName) {
|
|
26
|
+
// JSON mode requires source container name argument
|
|
27
|
+
if (options.json) {
|
|
28
|
+
console.log(JSON.stringify({ error: 'Source container name is required' }))
|
|
29
|
+
process.exit(1)
|
|
30
|
+
}
|
|
31
|
+
|
|
26
32
|
const containers = await containerManager.list()
|
|
27
33
|
const stopped = containers.filter((c) => c.status !== 'running')
|
|
28
34
|
|
|
@@ -57,7 +63,11 @@ export const cloneCommand = new Command('clone')
|
|
|
57
63
|
|
|
58
64
|
const sourceConfig = await containerManager.getConfig(sourceName)
|
|
59
65
|
if (!sourceConfig) {
|
|
60
|
-
|
|
66
|
+
if (options.json) {
|
|
67
|
+
console.log(JSON.stringify({ error: `Container "${sourceName}" not found` }))
|
|
68
|
+
} else {
|
|
69
|
+
console.error(uiError(`Container "${sourceName}" not found`))
|
|
70
|
+
}
|
|
61
71
|
process.exit(1)
|
|
62
72
|
}
|
|
63
73
|
|
|
@@ -65,15 +75,21 @@ export const cloneCommand = new Command('clone')
|
|
|
65
75
|
engine: sourceConfig.engine,
|
|
66
76
|
})
|
|
67
77
|
if (running) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
78
|
+
const errorMsg = `Container "${sourceName}" is running. Stop it first to clone.`
|
|
79
|
+
if (options.json) {
|
|
80
|
+
console.log(JSON.stringify({ error: errorMsg }))
|
|
81
|
+
} else {
|
|
82
|
+
console.error(uiError(errorMsg))
|
|
83
|
+
}
|
|
73
84
|
process.exit(1)
|
|
74
85
|
}
|
|
75
86
|
|
|
76
87
|
if (!targetName) {
|
|
88
|
+
// JSON mode requires target container name argument
|
|
89
|
+
if (options.json) {
|
|
90
|
+
console.log(JSON.stringify({ error: 'Target container name is required' }))
|
|
91
|
+
process.exit(1)
|
|
92
|
+
}
|
|
77
93
|
targetName = await promptContainerName(`${sourceName}-copy`)
|
|
78
94
|
}
|
|
79
95
|
|
|
@@ -83,7 +99,12 @@ export const cloneCommand = new Command('clone')
|
|
|
83
99
|
engine: sourceConfig.engine,
|
|
84
100
|
})
|
|
85
101
|
) {
|
|
86
|
-
|
|
102
|
+
const errorMsg = `Container "${targetName}" already exists`
|
|
103
|
+
if (options.json) {
|
|
104
|
+
console.log(JSON.stringify({ error: errorMsg }))
|
|
105
|
+
} else {
|
|
106
|
+
console.error(uiError(errorMsg))
|
|
107
|
+
}
|
|
87
108
|
process.exit(1)
|
|
88
109
|
}
|
|
89
110
|
|
|
@@ -121,7 +142,11 @@ export const cloneCommand = new Command('clone')
|
|
|
121
142
|
}
|
|
122
143
|
} catch (error) {
|
|
123
144
|
const e = error as Error
|
|
124
|
-
|
|
145
|
+
if (options.json) {
|
|
146
|
+
console.log(JSON.stringify({ error: e.message }))
|
|
147
|
+
} else {
|
|
148
|
+
console.error(uiError(e.message))
|
|
149
|
+
}
|
|
125
150
|
process.exit(1)
|
|
126
151
|
}
|
|
127
152
|
},
|