spindb 0.5.4 → 0.5.5
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 +27 -1
- package/cli/commands/backup.ts +263 -0
- package/cli/commands/config.ts +144 -66
- package/cli/commands/engines.ts +1 -9
- package/cli/commands/list.ts +42 -4
- package/cli/commands/menu.ts +191 -19
- package/cli/commands/restore.ts +3 -0
- package/cli/index.ts +2 -0
- package/cli/ui/prompts.ts +87 -0
- package/cli/ui/theme.ts +11 -0
- package/core/config-manager.ts +133 -37
- package/core/container-manager.ts +76 -2
- package/core/dependency-manager.ts +5 -0
- package/engines/base-engine.ts +20 -0
- package/engines/mysql/backup.ts +159 -0
- package/engines/mysql/index.ts +39 -0
- package/engines/mysql/restore.ts +16 -2
- package/engines/postgresql/backup.ts +93 -0
- package/engines/postgresql/index.ts +37 -0
- package/package.json +1 -1
- package/types/index.ts +20 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostgreSQL Backup
|
|
3
|
+
*
|
|
4
|
+
* Creates database backups in SQL or custom (.dump) format using pg_dump.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { spawn } from 'child_process'
|
|
8
|
+
import { stat } from 'fs/promises'
|
|
9
|
+
import { configManager } from '../../core/config-manager'
|
|
10
|
+
import { defaults } from '../../config/defaults'
|
|
11
|
+
import type { ContainerConfig, BackupOptions, BackupResult } from '../../types'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Get pg_dump path from config, with helpful error message
|
|
15
|
+
*/
|
|
16
|
+
async function getPgDumpPath(): Promise<string> {
|
|
17
|
+
const pgDumpPath = await configManager.getBinaryPath('pg_dump')
|
|
18
|
+
if (!pgDumpPath) {
|
|
19
|
+
throw new Error(
|
|
20
|
+
'pg_dump not found. Install PostgreSQL client tools:\n' +
|
|
21
|
+
' macOS: brew install libpq && brew link --force libpq\n' +
|
|
22
|
+
' Ubuntu/Debian: apt install postgresql-client\n\n' +
|
|
23
|
+
'Or configure manually: spindb config set pg_dump /path/to/pg_dump',
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
return pgDumpPath
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Create a backup of a PostgreSQL database
|
|
31
|
+
*
|
|
32
|
+
* CLI equivalent:
|
|
33
|
+
* - SQL format: pg_dump -Fp -h 127.0.0.1 -p {port} -U postgres -d {database} -f {outputPath}
|
|
34
|
+
* - Dump format: pg_dump -Fc -h 127.0.0.1 -p {port} -U postgres -d {database} -f {outputPath}
|
|
35
|
+
*/
|
|
36
|
+
export async function createBackup(
|
|
37
|
+
container: ContainerConfig,
|
|
38
|
+
outputPath: string,
|
|
39
|
+
options: BackupOptions,
|
|
40
|
+
): Promise<BackupResult> {
|
|
41
|
+
const { port } = container
|
|
42
|
+
const { database, format } = options
|
|
43
|
+
|
|
44
|
+
const pgDumpPath = await getPgDumpPath()
|
|
45
|
+
|
|
46
|
+
// -Fp = plain SQL format, -Fc = custom format
|
|
47
|
+
const formatFlag = format === 'sql' ? '-Fp' : '-Fc'
|
|
48
|
+
|
|
49
|
+
return new Promise((resolve, reject) => {
|
|
50
|
+
const args = [
|
|
51
|
+
'-h',
|
|
52
|
+
'127.0.0.1',
|
|
53
|
+
'-p',
|
|
54
|
+
String(port),
|
|
55
|
+
'-U',
|
|
56
|
+
defaults.superuser,
|
|
57
|
+
'-d',
|
|
58
|
+
database,
|
|
59
|
+
formatFlag,
|
|
60
|
+
'-f',
|
|
61
|
+
outputPath,
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
const proc = spawn(pgDumpPath, args, {
|
|
65
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
let stderr = ''
|
|
69
|
+
|
|
70
|
+
proc.stderr?.on('data', (data: Buffer) => {
|
|
71
|
+
stderr += data.toString()
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
proc.on('error', (err: NodeJS.ErrnoException) => {
|
|
75
|
+
reject(err)
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
proc.on('close', async (code) => {
|
|
79
|
+
if (code === 0) {
|
|
80
|
+
// Get file size
|
|
81
|
+
const stats = await stat(outputPath)
|
|
82
|
+
resolve({
|
|
83
|
+
path: outputPath,
|
|
84
|
+
format: format === 'sql' ? 'sql' : 'custom',
|
|
85
|
+
size: stats.size,
|
|
86
|
+
})
|
|
87
|
+
} else {
|
|
88
|
+
const errorMessage = stderr || `pg_dump exited with code ${code}`
|
|
89
|
+
reject(new Error(errorMessage))
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
})
|
|
93
|
+
}
|
|
@@ -16,10 +16,13 @@ import {
|
|
|
16
16
|
FALLBACK_VERSION_MAP,
|
|
17
17
|
} from './binary-urls'
|
|
18
18
|
import { detectBackupFormat, restoreBackup } from './restore'
|
|
19
|
+
import { createBackup } from './backup'
|
|
19
20
|
import type {
|
|
20
21
|
ContainerConfig,
|
|
21
22
|
ProgressCallback,
|
|
22
23
|
BackupFormat,
|
|
24
|
+
BackupOptions,
|
|
25
|
+
BackupResult,
|
|
23
26
|
RestoreResult,
|
|
24
27
|
DumpResult,
|
|
25
28
|
StatusResult,
|
|
@@ -369,6 +372,29 @@ export class PostgreSQLEngine extends BaseEngine {
|
|
|
369
372
|
}
|
|
370
373
|
}
|
|
371
374
|
|
|
375
|
+
/**
|
|
376
|
+
* Get the size of the container's database in bytes
|
|
377
|
+
* Uses pg_database_size() to get accurate data size
|
|
378
|
+
* Returns null if container is not running or query fails
|
|
379
|
+
*/
|
|
380
|
+
async getDatabaseSize(container: ContainerConfig): Promise<number | null> {
|
|
381
|
+
const { port, database } = container
|
|
382
|
+
const db = database || 'postgres'
|
|
383
|
+
|
|
384
|
+
try {
|
|
385
|
+
const psqlPath = await this.getPsqlPath()
|
|
386
|
+
// Query pg_database_size for the specific database
|
|
387
|
+
const { stdout } = await execAsync(
|
|
388
|
+
`"${psqlPath}" -h 127.0.0.1 -p ${port} -U ${defaults.superuser} -d postgres -t -A -c "SELECT pg_database_size('${db}')"`,
|
|
389
|
+
)
|
|
390
|
+
const size = parseInt(stdout.trim(), 10)
|
|
391
|
+
return isNaN(size) ? null : size
|
|
392
|
+
} catch {
|
|
393
|
+
// Container not running or query failed
|
|
394
|
+
return null
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
372
398
|
/**
|
|
373
399
|
* Create a dump from a remote database using a connection string
|
|
374
400
|
* @param connectionString PostgreSQL connection string (e.g., postgresql://user:pass@host:port/dbname)
|
|
@@ -420,6 +446,17 @@ export class PostgreSQLEngine extends BaseEngine {
|
|
|
420
446
|
})
|
|
421
447
|
})
|
|
422
448
|
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Create a backup of a PostgreSQL database
|
|
452
|
+
*/
|
|
453
|
+
async backup(
|
|
454
|
+
container: ContainerConfig,
|
|
455
|
+
outputPath: string,
|
|
456
|
+
options: BackupOptions,
|
|
457
|
+
): Promise<BackupResult> {
|
|
458
|
+
return createBackup(container, outputPath, options)
|
|
459
|
+
}
|
|
423
460
|
}
|
|
424
461
|
|
|
425
462
|
export const postgresqlEngine = new PostgreSQLEngine()
|
package/package.json
CHANGED
package/types/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ export type ContainerConfig = {
|
|
|
4
4
|
version: string
|
|
5
5
|
port: number
|
|
6
6
|
database: string
|
|
7
|
+
databases?: string[]
|
|
7
8
|
created: string
|
|
8
9
|
status: 'created' | 'running' | 'stopped'
|
|
9
10
|
clonedFrom?: string
|
|
@@ -56,6 +57,17 @@ export type RestoreResult = {
|
|
|
56
57
|
code?: number
|
|
57
58
|
}
|
|
58
59
|
|
|
60
|
+
export type BackupOptions = {
|
|
61
|
+
database: string
|
|
62
|
+
format: 'sql' | 'dump'
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export type BackupResult = {
|
|
66
|
+
path: string
|
|
67
|
+
format: string
|
|
68
|
+
size: number
|
|
69
|
+
}
|
|
70
|
+
|
|
59
71
|
export type DumpResult = {
|
|
60
72
|
filePath: string
|
|
61
73
|
stdout?: string
|
|
@@ -85,6 +97,10 @@ export type BinaryTool =
|
|
|
85
97
|
| 'mysqlpump'
|
|
86
98
|
| 'mysqld'
|
|
87
99
|
| 'mysqladmin'
|
|
100
|
+
// Enhanced shells (optional)
|
|
101
|
+
| 'pgcli'
|
|
102
|
+
| 'mycli'
|
|
103
|
+
| 'usql'
|
|
88
104
|
|
|
89
105
|
/**
|
|
90
106
|
* Source of a binary - bundled (downloaded by spindb) or system (found on PATH)
|
|
@@ -118,6 +134,10 @@ export type SpinDBConfig = {
|
|
|
118
134
|
mysqlpump?: BinaryConfig
|
|
119
135
|
mysqld?: BinaryConfig
|
|
120
136
|
mysqladmin?: BinaryConfig
|
|
137
|
+
// Enhanced shells (optional)
|
|
138
|
+
pgcli?: BinaryConfig
|
|
139
|
+
mycli?: BinaryConfig
|
|
140
|
+
usql?: BinaryConfig
|
|
121
141
|
}
|
|
122
142
|
// Default settings
|
|
123
143
|
defaults?: {
|