spindb 0.4.1 → 0.5.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 +77 -100
- package/cli/commands/clone.ts +3 -1
- package/cli/commands/connect.ts +50 -24
- package/cli/commands/create.ts +237 -156
- package/cli/commands/delete.ts +3 -1
- package/cli/commands/list.ts +14 -3
- package/cli/commands/menu.ts +103 -46
- package/cli/commands/restore.ts +56 -19
- package/cli/commands/start.ts +30 -4
- package/cli/commands/stop.ts +3 -1
- package/cli/ui/prompts.ts +94 -31
- package/config/defaults.ts +40 -15
- package/config/engine-defaults.ts +84 -0
- package/config/os-dependencies.ts +68 -19
- package/config/paths.ts +77 -22
- package/core/binary-manager.ts +30 -5
- package/core/container-manager.ts +124 -60
- package/core/port-manager.ts +42 -31
- package/core/process-manager.ts +14 -6
- package/engines/index.ts +7 -2
- package/engines/mysql/binary-detection.ts +248 -0
- package/engines/mysql/index.ts +699 -0
- package/engines/postgresql/index.ts +13 -6
- package/package.json +4 -2
- package/types/index.ts +29 -5
package/cli/commands/menu.ts
CHANGED
|
@@ -28,6 +28,7 @@ import { join } from 'path'
|
|
|
28
28
|
import { paths } from '../../config/paths'
|
|
29
29
|
import { portManager } from '../../core/port-manager'
|
|
30
30
|
import { defaults } from '../../config/defaults'
|
|
31
|
+
import type { EngineName } from '../../types'
|
|
31
32
|
import inquirer from 'inquirer'
|
|
32
33
|
import { getMissingDependencies } from '../../core/dependency-manager'
|
|
33
34
|
|
|
@@ -39,6 +40,14 @@ type MenuChoice =
|
|
|
39
40
|
}
|
|
40
41
|
| inquirer.Separator
|
|
41
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Engine icons for display
|
|
45
|
+
*/
|
|
46
|
+
const engineIcons: Record<string, string> = {
|
|
47
|
+
postgresql: '🐘',
|
|
48
|
+
mysql: '🐬',
|
|
49
|
+
}
|
|
50
|
+
|
|
42
51
|
async function showMainMenu(): Promise<void> {
|
|
43
52
|
console.clear()
|
|
44
53
|
console.log(header('SpinDB - Local Database Manager'))
|
|
@@ -57,7 +66,6 @@ async function showMainMenu(): Promise<void> {
|
|
|
57
66
|
|
|
58
67
|
const canStart = stopped > 0
|
|
59
68
|
const canStop = running > 0
|
|
60
|
-
const canConnect = running > 0
|
|
61
69
|
const canRestore = running > 0
|
|
62
70
|
const canClone = containers.length > 0
|
|
63
71
|
|
|
@@ -92,13 +100,6 @@ async function showMainMenu(): Promise<void> {
|
|
|
92
100
|
value: 'stop',
|
|
93
101
|
disabled: canStop ? false : 'No running containers',
|
|
94
102
|
},
|
|
95
|
-
{
|
|
96
|
-
name: canConnect
|
|
97
|
-
? `${chalk.blue('⌘')} Open psql shell`
|
|
98
|
-
: chalk.gray('⌘ Open psql shell'),
|
|
99
|
-
value: 'connect',
|
|
100
|
-
disabled: canConnect ? false : 'No running containers',
|
|
101
|
-
},
|
|
102
103
|
{
|
|
103
104
|
name: canRestore
|
|
104
105
|
? `${chalk.magenta('↓')} Restore backup`
|
|
@@ -147,9 +148,6 @@ async function showMainMenu(): Promise<void> {
|
|
|
147
148
|
case 'stop':
|
|
148
149
|
await handleStop()
|
|
149
150
|
break
|
|
150
|
-
case 'connect':
|
|
151
|
-
await handleConnect()
|
|
152
|
-
break
|
|
153
151
|
case 'restore':
|
|
154
152
|
await handleRestore()
|
|
155
153
|
break
|
|
@@ -246,7 +244,7 @@ async function handleCreate(): Promise<void> {
|
|
|
246
244
|
createSpinnerInstance.start()
|
|
247
245
|
|
|
248
246
|
await containerManager.create(containerName, {
|
|
249
|
-
engine: dbEngine.name,
|
|
247
|
+
engine: dbEngine.name as EngineName,
|
|
250
248
|
version,
|
|
251
249
|
port,
|
|
252
250
|
database,
|
|
@@ -362,6 +360,15 @@ async function handleList(): Promise<void> {
|
|
|
362
360
|
console.log(
|
|
363
361
|
info('No containers found. Create one with the "Create" option.'),
|
|
364
362
|
)
|
|
363
|
+
console.log()
|
|
364
|
+
|
|
365
|
+
await inquirer.prompt([
|
|
366
|
+
{
|
|
367
|
+
type: 'input',
|
|
368
|
+
name: 'continue',
|
|
369
|
+
message: chalk.gray('Press Enter to return to the main menu...'),
|
|
370
|
+
},
|
|
371
|
+
])
|
|
365
372
|
return
|
|
366
373
|
}
|
|
367
374
|
|
|
@@ -408,7 +415,7 @@ async function handleList(): Promise<void> {
|
|
|
408
415
|
console.log()
|
|
409
416
|
const containerChoices = [
|
|
410
417
|
...containers.map((c) => ({
|
|
411
|
-
name: `${c.name} ${chalk.gray(`(${c.engine} ${c.version}, port ${c.port})`)} ${
|
|
418
|
+
name: `${c.name} ${chalk.gray(`(${engineIcons[c.engine] || '🗄️'} ${c.engine} ${c.version}, port ${c.port})`)} ${
|
|
412
419
|
c.status === 'running'
|
|
413
420
|
? chalk.green('● running')
|
|
414
421
|
: chalk.gray('○ stopped')
|
|
@@ -447,7 +454,9 @@ async function showContainerSubmenu(containerName: string): Promise<void> {
|
|
|
447
454
|
}
|
|
448
455
|
|
|
449
456
|
// Check actual running state
|
|
450
|
-
const isRunning = await processManager.isRunning(containerName
|
|
457
|
+
const isRunning = await processManager.isRunning(containerName, {
|
|
458
|
+
engine: config.engine,
|
|
459
|
+
})
|
|
451
460
|
const status = isRunning ? 'running' : 'stopped'
|
|
452
461
|
|
|
453
462
|
console.clear()
|
|
@@ -465,6 +474,13 @@ async function showContainerSubmenu(containerName: string): Promise<void> {
|
|
|
465
474
|
!isRunning
|
|
466
475
|
? { name: `${chalk.green('▶')} Start container`, value: 'start' }
|
|
467
476
|
: { name: `${chalk.yellow('■')} Stop container`, value: 'stop' },
|
|
477
|
+
{
|
|
478
|
+
name: isRunning
|
|
479
|
+
? `${chalk.blue('⌘')} Open shell`
|
|
480
|
+
: chalk.gray('⌘ Open shell'),
|
|
481
|
+
value: 'shell',
|
|
482
|
+
disabled: isRunning ? false : 'Start container first',
|
|
483
|
+
},
|
|
468
484
|
{
|
|
469
485
|
name: !isRunning
|
|
470
486
|
? `${chalk.white('⚙')} Edit container`
|
|
@@ -504,6 +520,10 @@ async function showContainerSubmenu(containerName: string): Promise<void> {
|
|
|
504
520
|
await handleStopContainer(containerName)
|
|
505
521
|
await showContainerSubmenu(containerName)
|
|
506
522
|
return
|
|
523
|
+
case 'shell':
|
|
524
|
+
await handleOpenShell(containerName)
|
|
525
|
+
await showContainerSubmenu(containerName)
|
|
526
|
+
return
|
|
507
527
|
case 'edit': {
|
|
508
528
|
const newName = await handleEditContainer(containerName)
|
|
509
529
|
if (newName === null) {
|
|
@@ -674,21 +694,7 @@ async function handleCopyConnectionString(
|
|
|
674
694
|
}
|
|
675
695
|
}
|
|
676
696
|
|
|
677
|
-
async function
|
|
678
|
-
const containers = await containerManager.list()
|
|
679
|
-
const running = containers.filter((c) => c.status === 'running')
|
|
680
|
-
|
|
681
|
-
if (running.length === 0) {
|
|
682
|
-
console.log(warning('No running containers'))
|
|
683
|
-
return
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
const containerName = await promptContainerSelect(
|
|
687
|
-
running,
|
|
688
|
-
'Select container to connect to:',
|
|
689
|
-
)
|
|
690
|
-
if (!containerName) return
|
|
691
|
-
|
|
697
|
+
async function handleOpenShell(containerName: string): Promise<void> {
|
|
692
698
|
const config = await containerManager.getConfig(containerName)
|
|
693
699
|
if (!config) {
|
|
694
700
|
console.error(error(`Container "${containerName}" not found`))
|
|
@@ -701,25 +707,49 @@ async function handleConnect(): Promise<void> {
|
|
|
701
707
|
console.log(info(`Connecting to ${containerName}...`))
|
|
702
708
|
console.log()
|
|
703
709
|
|
|
704
|
-
//
|
|
705
|
-
|
|
710
|
+
// Determine shell command based on engine
|
|
711
|
+
let shellCmd: string
|
|
712
|
+
let shellArgs: string[]
|
|
713
|
+
let installHint: string
|
|
714
|
+
|
|
715
|
+
if (config.engine === 'mysql') {
|
|
716
|
+
shellCmd = 'mysql'
|
|
717
|
+
// MySQL connection: mysql -u root -h 127.0.0.1 -P port database
|
|
718
|
+
shellArgs = [
|
|
719
|
+
'-u',
|
|
720
|
+
'root',
|
|
721
|
+
'-h',
|
|
722
|
+
'127.0.0.1',
|
|
723
|
+
'-P',
|
|
724
|
+
String(config.port),
|
|
725
|
+
config.database,
|
|
726
|
+
]
|
|
727
|
+
installHint = 'brew install mysql-client'
|
|
728
|
+
} else {
|
|
729
|
+
// PostgreSQL (default)
|
|
730
|
+
shellCmd = 'psql'
|
|
731
|
+
shellArgs = [connectionString]
|
|
732
|
+
installHint = 'brew install libpq && brew link --force libpq'
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
const shellProcess = spawn(shellCmd, shellArgs, {
|
|
706
736
|
stdio: 'inherit',
|
|
707
737
|
})
|
|
708
738
|
|
|
709
|
-
|
|
739
|
+
shellProcess.on('error', (err: NodeJS.ErrnoException) => {
|
|
710
740
|
if (err.code === 'ENOENT') {
|
|
711
|
-
console.log(warning(
|
|
741
|
+
console.log(warning(`${shellCmd} not found on your system.`))
|
|
712
742
|
console.log()
|
|
713
743
|
console.log(chalk.gray(' Connect manually with:'))
|
|
714
744
|
console.log(chalk.cyan(` ${connectionString}`))
|
|
715
745
|
console.log()
|
|
716
|
-
console.log(chalk.gray(
|
|
717
|
-
console.log(chalk.cyan(
|
|
746
|
+
console.log(chalk.gray(` Install ${config.engine} client:`))
|
|
747
|
+
console.log(chalk.cyan(` ${installHint}`))
|
|
718
748
|
}
|
|
719
749
|
})
|
|
720
750
|
|
|
721
751
|
await new Promise<void>((resolve) => {
|
|
722
|
-
|
|
752
|
+
shellProcess.on('close', () => resolve())
|
|
723
753
|
})
|
|
724
754
|
}
|
|
725
755
|
|
|
@@ -779,7 +809,7 @@ async function handleCreateForRestore(): Promise<{
|
|
|
779
809
|
createSpinnerInstance.start()
|
|
780
810
|
|
|
781
811
|
await containerManager.create(containerName, {
|
|
782
|
-
engine: dbEngine.name,
|
|
812
|
+
engine: dbEngine.name as EngineName,
|
|
783
813
|
version,
|
|
784
814
|
port,
|
|
785
815
|
database,
|
|
@@ -836,7 +866,7 @@ async function handleRestore(): Promise<void> {
|
|
|
836
866
|
// Build choices: running containers + create new option
|
|
837
867
|
const choices = [
|
|
838
868
|
...running.map((c) => ({
|
|
839
|
-
name: `${c.name} ${chalk.gray(`(${c.engine} ${c.version}, port ${c.port})`)} ${chalk.green('● running')}`,
|
|
869
|
+
name: `${c.name} ${chalk.gray(`(${engineIcons[c.engine] || '🗄️'} ${c.engine} ${c.version}, port ${c.port})`)} ${chalk.green('● running')}`,
|
|
840
870
|
value: c.name,
|
|
841
871
|
short: c.name,
|
|
842
872
|
})),
|
|
@@ -1352,6 +1382,15 @@ async function handleStartContainer(containerName: string): Promise<void> {
|
|
|
1352
1382
|
`Port ${config.port} is in use. Stop the process using it or change this container's port.`,
|
|
1353
1383
|
),
|
|
1354
1384
|
)
|
|
1385
|
+
console.log()
|
|
1386
|
+
console.log(
|
|
1387
|
+
info(
|
|
1388
|
+
'Tip: If you installed MariaDB via apt, it may have started a system service.',
|
|
1389
|
+
),
|
|
1390
|
+
)
|
|
1391
|
+
console.log(
|
|
1392
|
+
info('Run: sudo systemctl stop mariadb && sudo systemctl disable mariadb'),
|
|
1393
|
+
)
|
|
1355
1394
|
return
|
|
1356
1395
|
}
|
|
1357
1396
|
|
|
@@ -1360,15 +1399,31 @@ async function handleStartContainer(containerName: string): Promise<void> {
|
|
|
1360
1399
|
const spinner = createSpinner(`Starting ${containerName}...`)
|
|
1361
1400
|
spinner.start()
|
|
1362
1401
|
|
|
1363
|
-
|
|
1364
|
-
|
|
1402
|
+
try {
|
|
1403
|
+
await engine.start(config)
|
|
1404
|
+
await containerManager.updateConfig(containerName, { status: 'running' })
|
|
1365
1405
|
|
|
1366
|
-
|
|
1406
|
+
spinner.succeed(`Container "${containerName}" started`)
|
|
1367
1407
|
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1408
|
+
const connectionString = engine.getConnectionString(config)
|
|
1409
|
+
console.log()
|
|
1410
|
+
console.log(chalk.gray(' Connection string:'))
|
|
1411
|
+
console.log(chalk.cyan(` ${connectionString}`))
|
|
1412
|
+
} catch (err) {
|
|
1413
|
+
spinner.fail(`Failed to start "${containerName}"`)
|
|
1414
|
+
const e = err as Error
|
|
1415
|
+
console.log()
|
|
1416
|
+
console.log(error(e.message))
|
|
1417
|
+
|
|
1418
|
+
// Check if there's a log file with more details
|
|
1419
|
+
const logPath = paths.getContainerLogPath(containerName, {
|
|
1420
|
+
engine: config.engine,
|
|
1421
|
+
})
|
|
1422
|
+
if (existsSync(logPath)) {
|
|
1423
|
+
console.log()
|
|
1424
|
+
console.log(info(`Check the log file for details: ${logPath}`))
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1372
1427
|
}
|
|
1373
1428
|
|
|
1374
1429
|
async function handleStopContainer(containerName: string): Promise<void> {
|
|
@@ -1566,7 +1621,9 @@ async function handleDelete(containerName: string): Promise<void> {
|
|
|
1566
1621
|
return
|
|
1567
1622
|
}
|
|
1568
1623
|
|
|
1569
|
-
const isRunning = await processManager.isRunning(containerName
|
|
1624
|
+
const isRunning = await processManager.isRunning(containerName, {
|
|
1625
|
+
engine: config.engine,
|
|
1626
|
+
})
|
|
1570
1627
|
|
|
1571
1628
|
if (isRunning) {
|
|
1572
1629
|
const stopSpinner = createSpinner(`Stopping ${containerName}...`)
|
package/cli/commands/restore.ts
CHANGED
|
@@ -76,8 +76,12 @@ export const restoreCommand = new Command('restore')
|
|
|
76
76
|
process.exit(1)
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
const { engine: engineName } = config
|
|
80
|
+
|
|
79
81
|
// Check if running
|
|
80
|
-
const running = await processManager.isRunning(containerName
|
|
82
|
+
const running = await processManager.isRunning(containerName, {
|
|
83
|
+
engine: engineName,
|
|
84
|
+
})
|
|
81
85
|
if (!running) {
|
|
82
86
|
console.error(
|
|
83
87
|
error(
|
|
@@ -88,7 +92,7 @@ export const restoreCommand = new Command('restore')
|
|
|
88
92
|
}
|
|
89
93
|
|
|
90
94
|
// Get engine
|
|
91
|
-
const engine = getEngine(
|
|
95
|
+
const engine = getEngine(engineName)
|
|
92
96
|
|
|
93
97
|
// Check for required client tools BEFORE doing anything
|
|
94
98
|
const depsSpinner = createSpinner('Checking required tools...')
|
|
@@ -129,14 +133,34 @@ export const restoreCommand = new Command('restore')
|
|
|
129
133
|
|
|
130
134
|
// Handle --from-url option
|
|
131
135
|
if (options.fromUrl) {
|
|
132
|
-
// Validate connection string
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
)
|
|
136
|
+
// Validate connection string matches container's engine
|
|
137
|
+
const isPgUrl =
|
|
138
|
+
options.fromUrl.startsWith('postgresql://') ||
|
|
139
|
+
options.fromUrl.startsWith('postgres://')
|
|
140
|
+
const isMysqlUrl = options.fromUrl.startsWith('mysql://')
|
|
141
|
+
|
|
142
|
+
if (engineName === 'postgresql' && !isPgUrl) {
|
|
143
|
+
console.error(
|
|
144
|
+
error(
|
|
145
|
+
'Connection string must start with postgresql:// or postgres:// for PostgreSQL containers',
|
|
146
|
+
),
|
|
147
|
+
)
|
|
148
|
+
process.exit(1)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (engineName === 'mysql' && !isMysqlUrl) {
|
|
137
152
|
console.error(
|
|
138
153
|
error(
|
|
139
|
-
'Connection string must start with
|
|
154
|
+
'Connection string must start with mysql:// for MySQL containers',
|
|
155
|
+
),
|
|
156
|
+
)
|
|
157
|
+
process.exit(1)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (!isPgUrl && !isMysqlUrl) {
|
|
161
|
+
console.error(
|
|
162
|
+
error(
|
|
163
|
+
'Connection string must start with postgresql://, postgres://, or mysql://',
|
|
140
164
|
),
|
|
141
165
|
)
|
|
142
166
|
process.exit(1)
|
|
@@ -167,11 +191,15 @@ export const restoreCommand = new Command('restore')
|
|
|
167
191
|
dumpSpinner.fail('Failed to create dump')
|
|
168
192
|
|
|
169
193
|
// Check if this is a missing tool error
|
|
194
|
+
const dumpTool = engineName === 'mysql' ? 'mysqldump' : 'pg_dump'
|
|
170
195
|
if (
|
|
171
|
-
e.message.includes(
|
|
196
|
+
e.message.includes(`${dumpTool} not found`) ||
|
|
172
197
|
e.message.includes('ENOENT')
|
|
173
198
|
) {
|
|
174
|
-
const installed = await promptInstallDependencies(
|
|
199
|
+
const installed = await promptInstallDependencies(
|
|
200
|
+
dumpTool,
|
|
201
|
+
engineName,
|
|
202
|
+
)
|
|
175
203
|
if (!installed) {
|
|
176
204
|
process.exit(1)
|
|
177
205
|
}
|
|
@@ -180,7 +208,7 @@ export const restoreCommand = new Command('restore')
|
|
|
180
208
|
}
|
|
181
209
|
|
|
182
210
|
console.log()
|
|
183
|
-
console.error(error(
|
|
211
|
+
console.error(error(`${dumpTool} error:`))
|
|
184
212
|
console.log(chalk.gray(` ${e.message}`))
|
|
185
213
|
process.exit(1)
|
|
186
214
|
}
|
|
@@ -313,14 +341,23 @@ export const restoreCommand = new Command('restore')
|
|
|
313
341
|
} catch (err) {
|
|
314
342
|
const e = err as Error
|
|
315
343
|
|
|
316
|
-
// Check if this is a missing tool error
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
344
|
+
// Check if this is a missing tool error (PostgreSQL or MySQL)
|
|
345
|
+
const missingToolPatterns = [
|
|
346
|
+
// PostgreSQL
|
|
347
|
+
'pg_restore not found',
|
|
348
|
+
'psql not found',
|
|
349
|
+
'pg_dump not found',
|
|
350
|
+
// MySQL
|
|
351
|
+
'mysql not found',
|
|
352
|
+
'mysqldump not found',
|
|
353
|
+
]
|
|
354
|
+
|
|
355
|
+
const matchingPattern = missingToolPatterns.find((p) =>
|
|
356
|
+
e.message.includes(p),
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
if (matchingPattern) {
|
|
360
|
+
const missingTool = matchingPattern.replace(' not found', '')
|
|
324
361
|
const installed = await promptInstallDependencies(missingTool)
|
|
325
362
|
if (installed) {
|
|
326
363
|
console.log(
|
package/cli/commands/start.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { containerManager } from '../../core/container-manager'
|
|
|
4
4
|
import { portManager } from '../../core/port-manager'
|
|
5
5
|
import { processManager } from '../../core/process-manager'
|
|
6
6
|
import { getEngine } from '../../engines'
|
|
7
|
+
import { getEngineDefaults } from '../../config/defaults'
|
|
7
8
|
import { promptContainerSelect } from '../ui/prompts'
|
|
8
9
|
import { createSpinner } from '../ui/spinner'
|
|
9
10
|
import { error, warning } from '../ui/theme'
|
|
@@ -46,18 +47,27 @@ export const startCommand = new Command('start')
|
|
|
46
47
|
process.exit(1)
|
|
47
48
|
}
|
|
48
49
|
|
|
50
|
+
const { engine: engineName } = config
|
|
51
|
+
|
|
49
52
|
// Check if already running
|
|
50
|
-
const running = await processManager.isRunning(containerName
|
|
53
|
+
const running = await processManager.isRunning(containerName, {
|
|
54
|
+
engine: engineName,
|
|
55
|
+
})
|
|
51
56
|
if (running) {
|
|
52
57
|
console.log(warning(`Container "${containerName}" is already running`))
|
|
53
58
|
return
|
|
54
59
|
}
|
|
55
60
|
|
|
61
|
+
// Get engine defaults for port range and database name
|
|
62
|
+
const engineDefaults = getEngineDefaults(engineName)
|
|
63
|
+
|
|
56
64
|
// Check port availability
|
|
57
65
|
const portAvailable = await portManager.isPortAvailable(config.port)
|
|
58
66
|
if (!portAvailable) {
|
|
59
|
-
// Try to find a new port
|
|
60
|
-
const { port: newPort } = await portManager.findAvailablePort(
|
|
67
|
+
// Try to find a new port (using engine-specific port range)
|
|
68
|
+
const { port: newPort } = await portManager.findAvailablePort({
|
|
69
|
+
portRange: engineDefaults.portRange,
|
|
70
|
+
})
|
|
61
71
|
console.log(
|
|
62
72
|
warning(
|
|
63
73
|
`Port ${config.port} is in use, switching to port ${newPort}`,
|
|
@@ -68,7 +78,7 @@ export const startCommand = new Command('start')
|
|
|
68
78
|
}
|
|
69
79
|
|
|
70
80
|
// Get engine and start
|
|
71
|
-
const engine = getEngine(
|
|
81
|
+
const engine = getEngine(engineName)
|
|
72
82
|
|
|
73
83
|
const spinner = createSpinner(`Starting ${containerName}...`)
|
|
74
84
|
spinner.start()
|
|
@@ -78,6 +88,22 @@ export const startCommand = new Command('start')
|
|
|
78
88
|
|
|
79
89
|
spinner.succeed(`Container "${containerName}" started`)
|
|
80
90
|
|
|
91
|
+
// Ensure the user's database exists (if different from default)
|
|
92
|
+
const defaultDb = engineDefaults.superuser // postgres or root
|
|
93
|
+
if (config.database && config.database !== defaultDb) {
|
|
94
|
+
const dbSpinner = createSpinner(
|
|
95
|
+
`Ensuring database "${config.database}" exists...`,
|
|
96
|
+
)
|
|
97
|
+
dbSpinner.start()
|
|
98
|
+
try {
|
|
99
|
+
await engine.createDatabase(config, config.database)
|
|
100
|
+
dbSpinner.succeed(`Database "${config.database}" ready`)
|
|
101
|
+
} catch {
|
|
102
|
+
// Database might already exist, which is fine
|
|
103
|
+
dbSpinner.succeed(`Database "${config.database}" ready`)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
81
107
|
// Show connection info
|
|
82
108
|
const connectionString = engine.getConnectionString(config)
|
|
83
109
|
console.log()
|
package/cli/commands/stop.ts
CHANGED
|
@@ -67,7 +67,9 @@ export const stopCommand = new Command('stop')
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
// Check if running
|
|
70
|
-
const running = await processManager.isRunning(containerName
|
|
70
|
+
const running = await processManager.isRunning(containerName, {
|
|
71
|
+
engine: config.engine,
|
|
72
|
+
})
|
|
71
73
|
if (!running) {
|
|
72
74
|
console.log(warning(`Container "${containerName}" is not running`))
|
|
73
75
|
return
|