spindb 0.9.1 → 0.9.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/cli/commands/backup.ts +13 -11
- package/cli/commands/clone.ts +14 -10
- package/cli/commands/config.ts +29 -29
- package/cli/commands/connect.ts +51 -39
- package/cli/commands/create.ts +59 -32
- package/cli/commands/delete.ts +8 -8
- package/cli/commands/deps.ts +17 -15
- package/cli/commands/doctor.ts +11 -11
- package/cli/commands/edit.ts +108 -55
- package/cli/commands/engines.ts +17 -15
- package/cli/commands/info.ts +8 -6
- package/cli/commands/list.ts +31 -16
- package/cli/commands/logs.ts +15 -11
- package/cli/commands/menu/backup-handlers.ts +52 -47
- package/cli/commands/menu/container-handlers.ts +120 -78
- package/cli/commands/menu/engine-handlers.ts +21 -11
- package/cli/commands/menu/index.ts +4 -4
- package/cli/commands/menu/shell-handlers.ts +34 -31
- package/cli/commands/menu/sql-handlers.ts +22 -16
- package/cli/commands/menu/update-handlers.ts +19 -17
- package/cli/commands/restore.ts +22 -20
- package/cli/commands/run.ts +20 -18
- package/cli/commands/self-update.ts +5 -5
- package/cli/commands/start.ts +11 -9
- package/cli/commands/stop.ts +9 -9
- package/cli/commands/url.ts +12 -9
- package/cli/helpers.ts +9 -4
- package/cli/ui/prompts.ts +12 -5
- package/cli/ui/spinner.ts +4 -4
- package/cli/ui/theme.ts +4 -4
- package/core/binary-manager.ts +5 -1
- package/core/container-manager.ts +5 -5
- package/core/platform-service.ts +3 -3
- package/core/start-with-retry.ts +6 -6
- package/core/transaction-manager.ts +6 -6
- package/engines/mysql/index.ts +11 -11
- package/engines/mysql/restore.ts +4 -4
- package/engines/mysql/version-validator.ts +2 -2
- package/engines/postgresql/binary-manager.ts +17 -17
- package/engines/postgresql/index.ts +7 -2
- package/engines/postgresql/restore.ts +2 -2
- package/engines/postgresql/version-validator.ts +2 -2
- package/engines/sqlite/index.ts +21 -8
- package/package.json +1 -1
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
import { platformService } from '../../../core/platform-service'
|
|
21
21
|
import { getEngine } from '../../../engines'
|
|
22
22
|
import { createSpinner } from '../../ui/spinner'
|
|
23
|
-
import {
|
|
23
|
+
import { uiError, uiWarning, uiInfo, uiSuccess } from '../../ui/theme'
|
|
24
24
|
import { pressEnterToContinue } from './shared'
|
|
25
25
|
|
|
26
26
|
export async function handleCopyConnectionString(
|
|
@@ -28,7 +28,7 @@ export async function handleCopyConnectionString(
|
|
|
28
28
|
): Promise<void> {
|
|
29
29
|
const config = await containerManager.getConfig(containerName)
|
|
30
30
|
if (!config) {
|
|
31
|
-
console.error(
|
|
31
|
+
console.error(uiError(`Container "${containerName}" not found`))
|
|
32
32
|
return
|
|
33
33
|
}
|
|
34
34
|
|
|
@@ -39,10 +39,10 @@ export async function handleCopyConnectionString(
|
|
|
39
39
|
|
|
40
40
|
console.log()
|
|
41
41
|
if (copied) {
|
|
42
|
-
console.log(
|
|
42
|
+
console.log(uiSuccess('Connection string copied to clipboard'))
|
|
43
43
|
console.log(chalk.gray(` ${connectionString}`))
|
|
44
44
|
} else {
|
|
45
|
-
console.log(
|
|
45
|
+
console.log(uiWarning('Could not copy to clipboard. Connection string:'))
|
|
46
46
|
console.log(chalk.cyan(` ${connectionString}`))
|
|
47
47
|
}
|
|
48
48
|
console.log()
|
|
@@ -59,7 +59,7 @@ export async function handleCopyConnectionString(
|
|
|
59
59
|
export async function handleOpenShell(containerName: string): Promise<void> {
|
|
60
60
|
const config = await containerManager.getConfig(containerName)
|
|
61
61
|
if (!config) {
|
|
62
|
-
console.error(
|
|
62
|
+
console.error(uiError(`Container "${containerName}" not found`))
|
|
63
63
|
return
|
|
64
64
|
}
|
|
65
65
|
|
|
@@ -69,12 +69,13 @@ export async function handleOpenShell(containerName: string): Promise<void> {
|
|
|
69
69
|
const shellCheckSpinner = createSpinner('Checking available shells...')
|
|
70
70
|
shellCheckSpinner.start()
|
|
71
71
|
|
|
72
|
-
const [usqlInstalled, pgcliInstalled, mycliInstalled, litecliInstalled] =
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
72
|
+
const [usqlInstalled, pgcliInstalled, mycliInstalled, litecliInstalled] =
|
|
73
|
+
await Promise.all([
|
|
74
|
+
isUsqlInstalled(),
|
|
75
|
+
isPgcliInstalled(),
|
|
76
|
+
isMycliInstalled(),
|
|
77
|
+
isLitecliInstalled(),
|
|
78
|
+
])
|
|
78
79
|
|
|
79
80
|
shellCheckSpinner.stop()
|
|
80
81
|
// Clear the spinner line
|
|
@@ -119,7 +120,9 @@ export async function handleOpenShell(containerName: string): Promise<void> {
|
|
|
119
120
|
engineSpecificInstallValue = 'install-pgcli'
|
|
120
121
|
}
|
|
121
122
|
|
|
122
|
-
const choices: Array<
|
|
123
|
+
const choices: Array<
|
|
124
|
+
{ name: string; value: ShellChoice } | inquirer.Separator
|
|
125
|
+
> = [
|
|
123
126
|
{
|
|
124
127
|
name: `>_ Use default shell (${defaultShellName})`,
|
|
125
128
|
value: 'default',
|
|
@@ -173,16 +176,16 @@ export async function handleOpenShell(containerName: string): Promise<void> {
|
|
|
173
176
|
|
|
174
177
|
if (shellChoice === 'install-pgcli') {
|
|
175
178
|
console.log()
|
|
176
|
-
console.log(
|
|
179
|
+
console.log(uiInfo('Installing pgcli for enhanced PostgreSQL shell...'))
|
|
177
180
|
const pm = await detectPackageManager()
|
|
178
181
|
if (pm) {
|
|
179
182
|
const result = await installPgcli(pm)
|
|
180
183
|
if (result.success) {
|
|
181
|
-
console.log(
|
|
184
|
+
console.log(uiSuccess('pgcli installed successfully!'))
|
|
182
185
|
console.log()
|
|
183
186
|
await launchShell(containerName, config, connectionString, 'pgcli')
|
|
184
187
|
} else {
|
|
185
|
-
console.error(
|
|
188
|
+
console.error(uiError(`Failed to install pgcli: ${result.error}`))
|
|
186
189
|
console.log()
|
|
187
190
|
console.log(chalk.gray('Manual installation:'))
|
|
188
191
|
for (const instruction of getPgcliManualInstructions()) {
|
|
@@ -192,7 +195,7 @@ export async function handleOpenShell(containerName: string): Promise<void> {
|
|
|
192
195
|
await pressEnterToContinue()
|
|
193
196
|
}
|
|
194
197
|
} else {
|
|
195
|
-
console.error(
|
|
198
|
+
console.error(uiError('No supported package manager found'))
|
|
196
199
|
console.log()
|
|
197
200
|
console.log(chalk.gray('Manual installation:'))
|
|
198
201
|
for (const instruction of getPgcliManualInstructions()) {
|
|
@@ -206,16 +209,16 @@ export async function handleOpenShell(containerName: string): Promise<void> {
|
|
|
206
209
|
|
|
207
210
|
if (shellChoice === 'install-mycli') {
|
|
208
211
|
console.log()
|
|
209
|
-
console.log(
|
|
212
|
+
console.log(uiInfo('Installing mycli for enhanced MySQL shell...'))
|
|
210
213
|
const pm = await detectPackageManager()
|
|
211
214
|
if (pm) {
|
|
212
215
|
const result = await installMycli(pm)
|
|
213
216
|
if (result.success) {
|
|
214
|
-
console.log(
|
|
217
|
+
console.log(uiSuccess('mycli installed successfully!'))
|
|
215
218
|
console.log()
|
|
216
219
|
await launchShell(containerName, config, connectionString, 'mycli')
|
|
217
220
|
} else {
|
|
218
|
-
console.error(
|
|
221
|
+
console.error(uiError(`Failed to install mycli: ${result.error}`))
|
|
219
222
|
console.log()
|
|
220
223
|
console.log(chalk.gray('Manual installation:'))
|
|
221
224
|
for (const instruction of getMycliManualInstructions()) {
|
|
@@ -225,7 +228,7 @@ export async function handleOpenShell(containerName: string): Promise<void> {
|
|
|
225
228
|
await pressEnterToContinue()
|
|
226
229
|
}
|
|
227
230
|
} else {
|
|
228
|
-
console.error(
|
|
231
|
+
console.error(uiError('No supported package manager found'))
|
|
229
232
|
console.log()
|
|
230
233
|
console.log(chalk.gray('Manual installation:'))
|
|
231
234
|
for (const instruction of getMycliManualInstructions()) {
|
|
@@ -239,16 +242,16 @@ export async function handleOpenShell(containerName: string): Promise<void> {
|
|
|
239
242
|
|
|
240
243
|
if (shellChoice === 'install-usql') {
|
|
241
244
|
console.log()
|
|
242
|
-
console.log(
|
|
245
|
+
console.log(uiInfo('Installing usql for enhanced shell experience...'))
|
|
243
246
|
const pm = await detectPackageManager()
|
|
244
247
|
if (pm) {
|
|
245
248
|
const result = await installUsql(pm)
|
|
246
249
|
if (result.success) {
|
|
247
|
-
console.log(
|
|
250
|
+
console.log(uiSuccess('usql installed successfully!'))
|
|
248
251
|
console.log()
|
|
249
252
|
await launchShell(containerName, config, connectionString, 'usql')
|
|
250
253
|
} else {
|
|
251
|
-
console.error(
|
|
254
|
+
console.error(uiError(`Failed to install usql: ${result.error}`))
|
|
252
255
|
console.log()
|
|
253
256
|
console.log(chalk.gray('Manual installation:'))
|
|
254
257
|
for (const instruction of getUsqlManualInstructions()) {
|
|
@@ -258,7 +261,7 @@ export async function handleOpenShell(containerName: string): Promise<void> {
|
|
|
258
261
|
await pressEnterToContinue()
|
|
259
262
|
}
|
|
260
263
|
} else {
|
|
261
|
-
console.error(
|
|
264
|
+
console.error(uiError('No supported package manager found'))
|
|
262
265
|
console.log()
|
|
263
266
|
console.log(chalk.gray('Manual installation:'))
|
|
264
267
|
for (const instruction of getUsqlManualInstructions()) {
|
|
@@ -272,16 +275,16 @@ export async function handleOpenShell(containerName: string): Promise<void> {
|
|
|
272
275
|
|
|
273
276
|
if (shellChoice === 'install-litecli') {
|
|
274
277
|
console.log()
|
|
275
|
-
console.log(
|
|
278
|
+
console.log(uiInfo('Installing litecli for enhanced SQLite shell...'))
|
|
276
279
|
const pm = await detectPackageManager()
|
|
277
280
|
if (pm) {
|
|
278
281
|
const result = await installLitecli(pm)
|
|
279
282
|
if (result.success) {
|
|
280
|
-
console.log(
|
|
283
|
+
console.log(uiSuccess('litecli installed successfully!'))
|
|
281
284
|
console.log()
|
|
282
285
|
await launchShell(containerName, config, connectionString, 'litecli')
|
|
283
286
|
} else {
|
|
284
|
-
console.error(
|
|
287
|
+
console.error(uiError(`Failed to install litecli: ${result.error}`))
|
|
285
288
|
console.log()
|
|
286
289
|
console.log(chalk.gray('Manual installation:'))
|
|
287
290
|
for (const instruction of getLitecliManualInstructions()) {
|
|
@@ -291,7 +294,7 @@ export async function handleOpenShell(containerName: string): Promise<void> {
|
|
|
291
294
|
await pressEnterToContinue()
|
|
292
295
|
}
|
|
293
296
|
} else {
|
|
294
|
-
console.error(
|
|
297
|
+
console.error(uiError('No supported package manager found'))
|
|
295
298
|
console.log()
|
|
296
299
|
console.log(chalk.gray('Manual installation:'))
|
|
297
300
|
for (const instruction of getLitecliManualInstructions()) {
|
|
@@ -312,7 +315,7 @@ async function launchShell(
|
|
|
312
315
|
connectionString: string,
|
|
313
316
|
shellType: 'default' | 'usql' | 'pgcli' | 'mycli' | 'litecli',
|
|
314
317
|
): Promise<void> {
|
|
315
|
-
console.log(
|
|
318
|
+
console.log(uiInfo(`Connecting to ${containerName}...`))
|
|
316
319
|
console.log()
|
|
317
320
|
|
|
318
321
|
let shellCmd: string
|
|
@@ -386,7 +389,7 @@ async function launchShell(
|
|
|
386
389
|
|
|
387
390
|
shellProcess.on('error', (err: NodeJS.ErrnoException) => {
|
|
388
391
|
if (err.code === 'ENOENT') {
|
|
389
|
-
console.log(
|
|
392
|
+
console.log(uiWarning(`${shellCmd} not found on your system.`))
|
|
390
393
|
console.log()
|
|
391
394
|
console.log(chalk.gray(' Connect manually with:'))
|
|
392
395
|
console.log(chalk.cyan(` ${connectionString}`))
|
|
@@ -394,7 +397,7 @@ async function launchShell(
|
|
|
394
397
|
console.log(chalk.gray(` Install ${shellCmd}:`))
|
|
395
398
|
console.log(chalk.cyan(` ${installHint}`))
|
|
396
399
|
} else {
|
|
397
|
-
console.log(
|
|
400
|
+
console.log(uiError(`Failed to start ${shellCmd}: ${err.message}`))
|
|
398
401
|
}
|
|
399
402
|
settle()
|
|
400
403
|
})
|
|
@@ -7,14 +7,17 @@ import { containerManager } from '../../../core/container-manager'
|
|
|
7
7
|
import { getMissingDependencies } from '../../../core/dependency-manager'
|
|
8
8
|
import { getEngine } from '../../../engines'
|
|
9
9
|
import { paths } from '../../../config/paths'
|
|
10
|
-
import {
|
|
11
|
-
|
|
10
|
+
import {
|
|
11
|
+
promptInstallDependencies,
|
|
12
|
+
promptDatabaseSelect,
|
|
13
|
+
} from '../../ui/prompts'
|
|
14
|
+
import { uiError, uiWarning, uiInfo, uiSuccess } from '../../ui/theme'
|
|
12
15
|
import { pressEnterToContinue } from './shared'
|
|
13
16
|
|
|
14
17
|
export async function handleRunSql(containerName: string): Promise<void> {
|
|
15
18
|
const config = await containerManager.getConfig(containerName)
|
|
16
19
|
if (!config) {
|
|
17
|
-
console.error(
|
|
20
|
+
console.error(uiError(`Container "${containerName}" not found`))
|
|
18
21
|
return
|
|
19
22
|
}
|
|
20
23
|
|
|
@@ -23,7 +26,7 @@ export async function handleRunSql(containerName: string): Promise<void> {
|
|
|
23
26
|
let missingDeps = await getMissingDependencies(config.engine)
|
|
24
27
|
if (missingDeps.length > 0) {
|
|
25
28
|
console.log(
|
|
26
|
-
|
|
29
|
+
uiWarning(`Missing tools: ${missingDeps.map((d) => d.name).join(', ')}`),
|
|
27
30
|
)
|
|
28
31
|
|
|
29
32
|
const installed = await promptInstallDependencies(
|
|
@@ -38,7 +41,7 @@ export async function handleRunSql(containerName: string): Promise<void> {
|
|
|
38
41
|
missingDeps = await getMissingDependencies(config.engine)
|
|
39
42
|
if (missingDeps.length > 0) {
|
|
40
43
|
console.log(
|
|
41
|
-
|
|
44
|
+
uiError(
|
|
42
45
|
`Still missing tools: ${missingDeps.map((d) => d.name).join(', ')}`,
|
|
43
46
|
),
|
|
44
47
|
)
|
|
@@ -50,11 +53,14 @@ export async function handleRunSql(containerName: string): Promise<void> {
|
|
|
50
53
|
}
|
|
51
54
|
|
|
52
55
|
// Strip quotes that terminals add when drag-and-dropping files
|
|
53
|
-
const stripQuotes = (path: string) =>
|
|
54
|
-
path.replace(/^['"]|['"]$/g, '').trim()
|
|
56
|
+
const stripQuotes = (path: string) => path.replace(/^['"]|['"]$/g, '').trim()
|
|
55
57
|
|
|
56
58
|
// Prompt for file path (empty input = go back)
|
|
57
|
-
console.log(
|
|
59
|
+
console.log(
|
|
60
|
+
chalk.gray(
|
|
61
|
+
' Drag & drop, enter path (abs or rel), or press Enter to go back',
|
|
62
|
+
),
|
|
63
|
+
)
|
|
58
64
|
const { filePath: rawFilePath } = await inquirer.prompt<{
|
|
59
65
|
filePath: string
|
|
60
66
|
}>([
|
|
@@ -90,7 +96,7 @@ export async function handleRunSql(containerName: string): Promise<void> {
|
|
|
90
96
|
}
|
|
91
97
|
|
|
92
98
|
console.log()
|
|
93
|
-
console.log(
|
|
99
|
+
console.log(uiInfo(`Running SQL file against "${databaseName}"...`))
|
|
94
100
|
console.log()
|
|
95
101
|
|
|
96
102
|
try {
|
|
@@ -99,11 +105,11 @@ export async function handleRunSql(containerName: string): Promise<void> {
|
|
|
99
105
|
database: databaseName,
|
|
100
106
|
})
|
|
101
107
|
console.log()
|
|
102
|
-
console.log(
|
|
103
|
-
} catch (
|
|
104
|
-
const e =
|
|
108
|
+
console.log(uiSuccess('SQL file executed successfully'))
|
|
109
|
+
} catch (error) {
|
|
110
|
+
const e = error as Error
|
|
105
111
|
console.log()
|
|
106
|
-
console.log(
|
|
112
|
+
console.log(uiError(`SQL execution failed: ${e.message}`))
|
|
107
113
|
}
|
|
108
114
|
|
|
109
115
|
console.log()
|
|
@@ -116,7 +122,7 @@ export async function handleRunSql(containerName: string): Promise<void> {
|
|
|
116
122
|
export async function handleViewLogs(containerName: string): Promise<void> {
|
|
117
123
|
const config = await containerManager.getConfig(containerName)
|
|
118
124
|
if (!config) {
|
|
119
|
-
console.error(
|
|
125
|
+
console.error(uiError(`Container "${containerName}" not found`))
|
|
120
126
|
return
|
|
121
127
|
}
|
|
122
128
|
|
|
@@ -126,7 +132,7 @@ export async function handleViewLogs(containerName: string): Promise<void> {
|
|
|
126
132
|
|
|
127
133
|
if (!existsSync(logPath)) {
|
|
128
134
|
console.log(
|
|
129
|
-
|
|
135
|
+
uiInfo(
|
|
130
136
|
`No log file found for "${containerName}". The container may not have been started yet.`,
|
|
131
137
|
),
|
|
132
138
|
)
|
|
@@ -194,7 +200,7 @@ export async function handleViewLogs(containerName: string): Promise<void> {
|
|
|
194
200
|
const lineCount = action === 'tail-100' ? 100 : 50
|
|
195
201
|
const content = await readFile(logPath, 'utf-8')
|
|
196
202
|
if (content.trim() === '') {
|
|
197
|
-
console.log(
|
|
203
|
+
console.log(uiInfo('Log file is empty'))
|
|
198
204
|
} else {
|
|
199
205
|
const lines = content.split('\n')
|
|
200
206
|
const nonEmptyLines =
|
|
@@ -9,7 +9,7 @@ import { paths } from '../../../config/paths'
|
|
|
9
9
|
import { getSupportedEngines } from '../../../config/engine-defaults'
|
|
10
10
|
import { checkEngineDependencies } from '../../../core/dependency-manager'
|
|
11
11
|
import { createSpinner } from '../../ui/spinner'
|
|
12
|
-
import { header,
|
|
12
|
+
import { header, uiSuccess, uiError, uiWarning, uiInfo } from '../../ui/theme'
|
|
13
13
|
import { pressEnterToContinue } from './shared'
|
|
14
14
|
import { Engine } from '../../../types'
|
|
15
15
|
|
|
@@ -26,7 +26,7 @@ export async function handleCheckUpdate(): Promise<void> {
|
|
|
26
26
|
if (!result) {
|
|
27
27
|
spinner.fail('Could not reach npm registry')
|
|
28
28
|
console.log()
|
|
29
|
-
console.log(
|
|
29
|
+
console.log(uiInfo('Check your internet connection and try again.'))
|
|
30
30
|
console.log(chalk.gray(' Manual update: npm install -g spindb@latest'))
|
|
31
31
|
console.log()
|
|
32
32
|
await pressEnterToContinue()
|
|
@@ -66,27 +66,29 @@ export async function handleCheckUpdate(): Promise<void> {
|
|
|
66
66
|
updateSpinner.succeed('Update complete')
|
|
67
67
|
console.log()
|
|
68
68
|
console.log(
|
|
69
|
-
|
|
69
|
+
uiSuccess(
|
|
70
70
|
`Updated from ${updateResult.previousVersion} to ${updateResult.newVersion}`,
|
|
71
71
|
),
|
|
72
72
|
)
|
|
73
73
|
console.log()
|
|
74
74
|
if (updateResult.previousVersion !== updateResult.newVersion) {
|
|
75
|
-
console.log(
|
|
75
|
+
console.log(
|
|
76
|
+
uiWarning('Please restart spindb to use the new version.'),
|
|
77
|
+
)
|
|
76
78
|
console.log()
|
|
77
79
|
}
|
|
78
80
|
} else {
|
|
79
81
|
updateSpinner.fail('Update failed')
|
|
80
82
|
console.log()
|
|
81
|
-
console.log(
|
|
83
|
+
console.log(uiError(updateResult.error || 'Unknown error'))
|
|
82
84
|
console.log()
|
|
83
|
-
console.log(
|
|
85
|
+
console.log(uiInfo('Manual update: npm install -g spindb@latest'))
|
|
84
86
|
}
|
|
85
87
|
await pressEnterToContinue()
|
|
86
88
|
} else if (action === 'disable') {
|
|
87
89
|
await updateManager.setAutoCheckEnabled(false)
|
|
88
90
|
console.log()
|
|
89
|
-
console.log(
|
|
91
|
+
console.log(uiInfo('Update checks disabled on startup.'))
|
|
90
92
|
console.log(chalk.gray(' Re-enable with: spindb config update-check on'))
|
|
91
93
|
console.log()
|
|
92
94
|
await pressEnterToContinue()
|
|
@@ -138,7 +140,7 @@ async function checkConfiguration(): Promise<HealthCheckResult> {
|
|
|
138
140
|
label: 'Refresh binary cache',
|
|
139
141
|
handler: async () => {
|
|
140
142
|
await configManager.refreshAllBinaries()
|
|
141
|
-
console.log(
|
|
143
|
+
console.log(uiSuccess('Binary cache refreshed'))
|
|
142
144
|
},
|
|
143
145
|
},
|
|
144
146
|
}
|
|
@@ -150,12 +152,12 @@ async function checkConfiguration(): Promise<HealthCheckResult> {
|
|
|
150
152
|
message: 'Configuration valid',
|
|
151
153
|
details: [`Binary tools cached: ${binaryCount}`],
|
|
152
154
|
}
|
|
153
|
-
} catch (
|
|
155
|
+
} catch (error) {
|
|
154
156
|
return {
|
|
155
157
|
name: 'Configuration',
|
|
156
158
|
status: 'error',
|
|
157
159
|
message: 'Configuration file is corrupted',
|
|
158
|
-
details: [(
|
|
160
|
+
details: [(error as Error).message],
|
|
159
161
|
}
|
|
160
162
|
}
|
|
161
163
|
}
|
|
@@ -199,12 +201,12 @@ async function checkContainers(): Promise<HealthCheckResult> {
|
|
|
199
201
|
message: `${containers.length} container(s)`,
|
|
200
202
|
details,
|
|
201
203
|
}
|
|
202
|
-
} catch (
|
|
204
|
+
} catch (error) {
|
|
203
205
|
return {
|
|
204
206
|
name: 'Containers',
|
|
205
207
|
status: 'error',
|
|
206
208
|
message: 'Failed to list containers',
|
|
207
|
-
details: [(
|
|
209
|
+
details: [(error as Error).message],
|
|
208
210
|
}
|
|
209
211
|
}
|
|
210
212
|
}
|
|
@@ -233,7 +235,7 @@ async function checkSqliteRegistry(): Promise<HealthCheckResult> {
|
|
|
233
235
|
label: 'Remove orphaned entries from registry',
|
|
234
236
|
handler: async () => {
|
|
235
237
|
const count = await sqliteRegistry.removeOrphans()
|
|
236
|
-
console.log(
|
|
238
|
+
console.log(uiSuccess(`Removed ${count} orphaned entries`))
|
|
237
239
|
},
|
|
238
240
|
},
|
|
239
241
|
}
|
|
@@ -244,12 +246,12 @@ async function checkSqliteRegistry(): Promise<HealthCheckResult> {
|
|
|
244
246
|
status: 'ok',
|
|
245
247
|
message: `${entries.length} database(s) registered, all files exist`,
|
|
246
248
|
}
|
|
247
|
-
} catch (
|
|
249
|
+
} catch (error) {
|
|
248
250
|
return {
|
|
249
251
|
name: 'SQLite Registry',
|
|
250
252
|
status: 'warning',
|
|
251
253
|
message: 'Could not check registry',
|
|
252
|
-
details: [(
|
|
254
|
+
details: [(error as Error).message],
|
|
253
255
|
}
|
|
254
256
|
}
|
|
255
257
|
}
|
|
@@ -279,12 +281,12 @@ async function checkBinaries(): Promise<HealthCheckResult> {
|
|
|
279
281
|
message: hasWarning ? 'Some tools missing' : 'All tools available',
|
|
280
282
|
details: results,
|
|
281
283
|
}
|
|
282
|
-
} catch (
|
|
284
|
+
} catch (error) {
|
|
283
285
|
return {
|
|
284
286
|
name: 'Database Tools',
|
|
285
287
|
status: 'error',
|
|
286
288
|
message: 'Failed to check tools',
|
|
287
|
-
details: [(
|
|
289
|
+
details: [(error as Error).message],
|
|
288
290
|
}
|
|
289
291
|
}
|
|
290
292
|
}
|
package/cli/commands/restore.ts
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
promptInstallDependencies,
|
|
12
12
|
} from '../ui/prompts'
|
|
13
13
|
import { createSpinner } from '../ui/spinner'
|
|
14
|
-
import {
|
|
14
|
+
import { uiSuccess, uiError, uiWarning } from '../ui/theme'
|
|
15
15
|
import { tmpdir } from 'os'
|
|
16
16
|
import { join } from 'path'
|
|
17
17
|
import { getMissingDependencies } from '../../core/dependency-manager'
|
|
@@ -50,11 +50,13 @@ export const restoreCommand = new Command('restore')
|
|
|
50
50
|
if (running.length === 0) {
|
|
51
51
|
if (containers.length === 0) {
|
|
52
52
|
console.log(
|
|
53
|
-
|
|
53
|
+
uiWarning(
|
|
54
|
+
'No containers found. Create one with: spindb create',
|
|
55
|
+
),
|
|
54
56
|
)
|
|
55
57
|
} else {
|
|
56
58
|
console.log(
|
|
57
|
-
|
|
59
|
+
uiWarning(
|
|
58
60
|
'No running containers. Start one first with: spindb start',
|
|
59
61
|
),
|
|
60
62
|
)
|
|
@@ -72,7 +74,7 @@ export const restoreCommand = new Command('restore')
|
|
|
72
74
|
|
|
73
75
|
const config = await containerManager.getConfig(containerName)
|
|
74
76
|
if (!config) {
|
|
75
|
-
console.error(
|
|
77
|
+
console.error(uiError(`Container "${containerName}" not found`))
|
|
76
78
|
process.exit(1)
|
|
77
79
|
}
|
|
78
80
|
|
|
@@ -83,7 +85,7 @@ export const restoreCommand = new Command('restore')
|
|
|
83
85
|
})
|
|
84
86
|
if (!running) {
|
|
85
87
|
console.error(
|
|
86
|
-
|
|
88
|
+
uiError(
|
|
87
89
|
`Container "${containerName}" is not running. Start it first.`,
|
|
88
90
|
),
|
|
89
91
|
)
|
|
@@ -113,7 +115,7 @@ export const restoreCommand = new Command('restore')
|
|
|
113
115
|
missingDeps = await getMissingDependencies(config.engine)
|
|
114
116
|
if (missingDeps.length > 0) {
|
|
115
117
|
console.error(
|
|
116
|
-
|
|
118
|
+
uiError(
|
|
117
119
|
`Still missing tools: ${missingDeps.map((d) => d.name).join(', ')}`,
|
|
118
120
|
),
|
|
119
121
|
)
|
|
@@ -134,7 +136,7 @@ export const restoreCommand = new Command('restore')
|
|
|
134
136
|
|
|
135
137
|
if (engineName === 'postgresql' && !isPgUrl) {
|
|
136
138
|
console.error(
|
|
137
|
-
|
|
139
|
+
uiError(
|
|
138
140
|
'Connection string must start with postgresql:// or postgres:// for PostgreSQL containers',
|
|
139
141
|
),
|
|
140
142
|
)
|
|
@@ -143,7 +145,7 @@ export const restoreCommand = new Command('restore')
|
|
|
143
145
|
|
|
144
146
|
if (engineName === 'mysql' && !isMysqlUrl) {
|
|
145
147
|
console.error(
|
|
146
|
-
|
|
148
|
+
uiError(
|
|
147
149
|
'Connection string must start with mysql:// for MySQL containers',
|
|
148
150
|
),
|
|
149
151
|
)
|
|
@@ -152,7 +154,7 @@ export const restoreCommand = new Command('restore')
|
|
|
152
154
|
|
|
153
155
|
if (!isPgUrl && !isMysqlUrl) {
|
|
154
156
|
console.error(
|
|
155
|
-
|
|
157
|
+
uiError(
|
|
156
158
|
'Connection string must start with postgresql://, postgres://, or mysql://',
|
|
157
159
|
),
|
|
158
160
|
)
|
|
@@ -181,8 +183,8 @@ export const restoreCommand = new Command('restore')
|
|
|
181
183
|
dumpSpinner.succeed('Dump created from remote database')
|
|
182
184
|
backupPath = tempDumpPath
|
|
183
185
|
dumpSuccess = true
|
|
184
|
-
} catch (
|
|
185
|
-
const e =
|
|
186
|
+
} catch (error) {
|
|
187
|
+
const e = error as Error
|
|
186
188
|
dumpSpinner.fail('Failed to create dump')
|
|
187
189
|
|
|
188
190
|
const dumpTool = engineName === 'mysql' ? 'mysqldump' : 'pg_dump'
|
|
@@ -201,19 +203,19 @@ export const restoreCommand = new Command('restore')
|
|
|
201
203
|
}
|
|
202
204
|
|
|
203
205
|
console.log()
|
|
204
|
-
console.error(
|
|
206
|
+
console.error(uiError(`${dumpTool} error:`))
|
|
205
207
|
console.log(chalk.gray(` ${e.message}`))
|
|
206
208
|
process.exit(1)
|
|
207
209
|
}
|
|
208
210
|
}
|
|
209
211
|
|
|
210
212
|
if (!dumpSuccess) {
|
|
211
|
-
console.error(
|
|
213
|
+
console.error(uiError('Failed to create dump after retries'))
|
|
212
214
|
process.exit(1)
|
|
213
215
|
}
|
|
214
216
|
} else {
|
|
215
217
|
if (!backupPath) {
|
|
216
|
-
console.error(
|
|
218
|
+
console.error(uiError('Backup file path is required'))
|
|
217
219
|
console.log(
|
|
218
220
|
chalk.gray(' Usage: spindb restore <container> <backup-file>'),
|
|
219
221
|
)
|
|
@@ -226,7 +228,7 @@ export const restoreCommand = new Command('restore')
|
|
|
226
228
|
}
|
|
227
229
|
|
|
228
230
|
if (!existsSync(backupPath)) {
|
|
229
|
-
console.error(
|
|
231
|
+
console.error(uiError(`Backup file not found: ${backupPath}`))
|
|
230
232
|
process.exit(1)
|
|
231
233
|
}
|
|
232
234
|
}
|
|
@@ -237,7 +239,7 @@ export const restoreCommand = new Command('restore')
|
|
|
237
239
|
}
|
|
238
240
|
|
|
239
241
|
if (!backupPath) {
|
|
240
|
-
console.error(
|
|
242
|
+
console.error(uiError('No backup path specified'))
|
|
241
243
|
process.exit(1)
|
|
242
244
|
}
|
|
243
245
|
|
|
@@ -347,7 +349,7 @@ export const restoreCommand = new Command('restore')
|
|
|
347
349
|
databaseName,
|
|
348
350
|
)
|
|
349
351
|
console.log()
|
|
350
|
-
console.log(
|
|
352
|
+
console.log(uiSuccess(`Database "${databaseName}" restored`))
|
|
351
353
|
console.log()
|
|
352
354
|
console.log(chalk.gray(' Connection string:'))
|
|
353
355
|
console.log(chalk.cyan(` ${connectionString}`))
|
|
@@ -365,8 +367,8 @@ export const restoreCommand = new Command('restore')
|
|
|
365
367
|
chalk.cyan(` spindb connect ${containerName} -d ${databaseName}`),
|
|
366
368
|
)
|
|
367
369
|
console.log()
|
|
368
|
-
} catch (
|
|
369
|
-
const e =
|
|
370
|
+
} catch (error) {
|
|
371
|
+
const e = error as Error
|
|
370
372
|
|
|
371
373
|
const missingToolPatterns = [
|
|
372
374
|
'pg_restore not found',
|
|
@@ -391,7 +393,7 @@ export const restoreCommand = new Command('restore')
|
|
|
391
393
|
process.exit(1)
|
|
392
394
|
}
|
|
393
395
|
|
|
394
|
-
console.error(
|
|
396
|
+
console.error(uiError(e.message))
|
|
395
397
|
process.exit(1)
|
|
396
398
|
} finally {
|
|
397
399
|
if (tempDumpPath) {
|