spindb 0.9.0 → 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/README.md +7 -0
- package/cli/commands/backup.ts +13 -11
- package/cli/commands/clone.ts +18 -8
- package/cli/commands/config.ts +29 -29
- package/cli/commands/connect.ts +51 -39
- package/cli/commands/create.ts +120 -43
- package/cli/commands/delete.ts +8 -8
- package/cli/commands/deps.ts +17 -15
- package/cli/commands/doctor.ts +16 -15
- package/cli/commands/edit.ts +115 -60
- package/cli/commands/engines.ts +50 -17
- package/cli/commands/info.ts +12 -8
- package/cli/commands/list.ts +34 -19
- package/cli/commands/logs.ts +24 -14
- package/cli/commands/menu/backup-handlers.ts +72 -49
- package/cli/commands/menu/container-handlers.ts +140 -80
- package/cli/commands/menu/engine-handlers.ts +145 -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 +105 -43
- 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 +49 -4
- package/cli/ui/prompts.ts +21 -8
- 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 +81 -30
- package/core/error-handler.ts +31 -0
- package/core/platform-service.ts +3 -3
- package/core/port-manager.ts +2 -0
- package/core/process-manager.ts +25 -3
- package/core/start-with-retry.ts +6 -6
- package/core/transaction-manager.ts +6 -6
- package/engines/mysql/backup.ts +53 -36
- package/engines/mysql/index.ts +59 -16
- 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 +13 -2
- package/engines/postgresql/restore.ts +2 -2
- package/engines/postgresql/version-validator.ts +2 -2
- package/engines/sqlite/index.ts +31 -9
- package/package.json +1 -1
|
@@ -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,11 +11,13 @@ 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'
|
|
18
18
|
import { platformService } from '../../core/platform-service'
|
|
19
|
+
import { TransactionManager } from '../../core/transaction-manager'
|
|
20
|
+
import { logDebug } from '../../core/error-handler'
|
|
19
21
|
|
|
20
22
|
export const restoreCommand = new Command('restore')
|
|
21
23
|
.description('Restore a backup to a container')
|
|
@@ -48,11 +50,13 @@ export const restoreCommand = new Command('restore')
|
|
|
48
50
|
if (running.length === 0) {
|
|
49
51
|
if (containers.length === 0) {
|
|
50
52
|
console.log(
|
|
51
|
-
|
|
53
|
+
uiWarning(
|
|
54
|
+
'No containers found. Create one with: spindb create',
|
|
55
|
+
),
|
|
52
56
|
)
|
|
53
57
|
} else {
|
|
54
58
|
console.log(
|
|
55
|
-
|
|
59
|
+
uiWarning(
|
|
56
60
|
'No running containers. Start one first with: spindb start',
|
|
57
61
|
),
|
|
58
62
|
)
|
|
@@ -70,7 +74,7 @@ export const restoreCommand = new Command('restore')
|
|
|
70
74
|
|
|
71
75
|
const config = await containerManager.getConfig(containerName)
|
|
72
76
|
if (!config) {
|
|
73
|
-
console.error(
|
|
77
|
+
console.error(uiError(`Container "${containerName}" not found`))
|
|
74
78
|
process.exit(1)
|
|
75
79
|
}
|
|
76
80
|
|
|
@@ -81,7 +85,7 @@ export const restoreCommand = new Command('restore')
|
|
|
81
85
|
})
|
|
82
86
|
if (!running) {
|
|
83
87
|
console.error(
|
|
84
|
-
|
|
88
|
+
uiError(
|
|
85
89
|
`Container "${containerName}" is not running. Start it first.`,
|
|
86
90
|
),
|
|
87
91
|
)
|
|
@@ -111,7 +115,7 @@ export const restoreCommand = new Command('restore')
|
|
|
111
115
|
missingDeps = await getMissingDependencies(config.engine)
|
|
112
116
|
if (missingDeps.length > 0) {
|
|
113
117
|
console.error(
|
|
114
|
-
|
|
118
|
+
uiError(
|
|
115
119
|
`Still missing tools: ${missingDeps.map((d) => d.name).join(', ')}`,
|
|
116
120
|
),
|
|
117
121
|
)
|
|
@@ -132,7 +136,7 @@ export const restoreCommand = new Command('restore')
|
|
|
132
136
|
|
|
133
137
|
if (engineName === 'postgresql' && !isPgUrl) {
|
|
134
138
|
console.error(
|
|
135
|
-
|
|
139
|
+
uiError(
|
|
136
140
|
'Connection string must start with postgresql:// or postgres:// for PostgreSQL containers',
|
|
137
141
|
),
|
|
138
142
|
)
|
|
@@ -141,7 +145,7 @@ export const restoreCommand = new Command('restore')
|
|
|
141
145
|
|
|
142
146
|
if (engineName === 'mysql' && !isMysqlUrl) {
|
|
143
147
|
console.error(
|
|
144
|
-
|
|
148
|
+
uiError(
|
|
145
149
|
'Connection string must start with mysql:// for MySQL containers',
|
|
146
150
|
),
|
|
147
151
|
)
|
|
@@ -150,7 +154,7 @@ export const restoreCommand = new Command('restore')
|
|
|
150
154
|
|
|
151
155
|
if (!isPgUrl && !isMysqlUrl) {
|
|
152
156
|
console.error(
|
|
153
|
-
|
|
157
|
+
uiError(
|
|
154
158
|
'Connection string must start with postgresql://, postgres://, or mysql://',
|
|
155
159
|
),
|
|
156
160
|
)
|
|
@@ -179,8 +183,8 @@ export const restoreCommand = new Command('restore')
|
|
|
179
183
|
dumpSpinner.succeed('Dump created from remote database')
|
|
180
184
|
backupPath = tempDumpPath
|
|
181
185
|
dumpSuccess = true
|
|
182
|
-
} catch (
|
|
183
|
-
const e =
|
|
186
|
+
} catch (error) {
|
|
187
|
+
const e = error as Error
|
|
184
188
|
dumpSpinner.fail('Failed to create dump')
|
|
185
189
|
|
|
186
190
|
const dumpTool = engineName === 'mysql' ? 'mysqldump' : 'pg_dump'
|
|
@@ -199,19 +203,19 @@ export const restoreCommand = new Command('restore')
|
|
|
199
203
|
}
|
|
200
204
|
|
|
201
205
|
console.log()
|
|
202
|
-
console.error(
|
|
206
|
+
console.error(uiError(`${dumpTool} error:`))
|
|
203
207
|
console.log(chalk.gray(` ${e.message}`))
|
|
204
208
|
process.exit(1)
|
|
205
209
|
}
|
|
206
210
|
}
|
|
207
211
|
|
|
208
212
|
if (!dumpSuccess) {
|
|
209
|
-
console.error(
|
|
213
|
+
console.error(uiError('Failed to create dump after retries'))
|
|
210
214
|
process.exit(1)
|
|
211
215
|
}
|
|
212
216
|
} else {
|
|
213
217
|
if (!backupPath) {
|
|
214
|
-
console.error(
|
|
218
|
+
console.error(uiError('Backup file path is required'))
|
|
215
219
|
console.log(
|
|
216
220
|
chalk.gray(' Usage: spindb restore <container> <backup-file>'),
|
|
217
221
|
)
|
|
@@ -224,7 +228,7 @@ export const restoreCommand = new Command('restore')
|
|
|
224
228
|
}
|
|
225
229
|
|
|
226
230
|
if (!existsSync(backupPath)) {
|
|
227
|
-
console.error(
|
|
231
|
+
console.error(uiError(`Backup file not found: ${backupPath}`))
|
|
228
232
|
process.exit(1)
|
|
229
233
|
}
|
|
230
234
|
}
|
|
@@ -235,7 +239,7 @@ export const restoreCommand = new Command('restore')
|
|
|
235
239
|
}
|
|
236
240
|
|
|
237
241
|
if (!backupPath) {
|
|
238
|
-
console.error(
|
|
242
|
+
console.error(uiError('No backup path specified'))
|
|
239
243
|
process.exit(1)
|
|
240
244
|
}
|
|
241
245
|
|
|
@@ -245,41 +249,99 @@ export const restoreCommand = new Command('restore')
|
|
|
245
249
|
const format = await engine.detectBackupFormat(backupPath)
|
|
246
250
|
detectSpinner.succeed(`Detected: ${format.description}`)
|
|
247
251
|
|
|
252
|
+
// Use TransactionManager to ensure database is cleaned up on restore failure
|
|
253
|
+
const tx = new TransactionManager()
|
|
254
|
+
let databaseCreated = false
|
|
255
|
+
|
|
248
256
|
const dbSpinner = createSpinner(
|
|
249
257
|
`Creating database "${databaseName}"...`,
|
|
250
258
|
)
|
|
251
259
|
dbSpinner.start()
|
|
252
260
|
|
|
253
|
-
|
|
254
|
-
|
|
261
|
+
try {
|
|
262
|
+
await engine.createDatabase(config, databaseName)
|
|
263
|
+
databaseCreated = true
|
|
264
|
+
dbSpinner.succeed(`Database "${databaseName}" ready`)
|
|
265
|
+
|
|
266
|
+
// Register rollback to drop database if restore fails
|
|
267
|
+
tx.addRollback({
|
|
268
|
+
description: `Drop database "${databaseName}"`,
|
|
269
|
+
execute: async () => {
|
|
270
|
+
try {
|
|
271
|
+
await engine.dropDatabase(config, databaseName)
|
|
272
|
+
logDebug(`Rolled back: dropped database "${databaseName}"`)
|
|
273
|
+
} catch (dropErr) {
|
|
274
|
+
logDebug(
|
|
275
|
+
`Failed to drop database during rollback: ${dropErr instanceof Error ? dropErr.message : String(dropErr)}`,
|
|
276
|
+
)
|
|
277
|
+
}
|
|
278
|
+
},
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
await containerManager.addDatabase(containerName, databaseName)
|
|
282
|
+
|
|
283
|
+
// Register rollback to remove database from container tracking
|
|
284
|
+
tx.addRollback({
|
|
285
|
+
description: `Remove "${databaseName}" from container tracking`,
|
|
286
|
+
execute: async () => {
|
|
287
|
+
try {
|
|
288
|
+
await containerManager.removeDatabase(
|
|
289
|
+
containerName,
|
|
290
|
+
databaseName,
|
|
291
|
+
)
|
|
292
|
+
logDebug(
|
|
293
|
+
`Rolled back: removed "${databaseName}" from container tracking`,
|
|
294
|
+
)
|
|
295
|
+
} catch (removeErr) {
|
|
296
|
+
logDebug(
|
|
297
|
+
`Failed to remove database from tracking during rollback: ${removeErr instanceof Error ? removeErr.message : String(removeErr)}`,
|
|
298
|
+
)
|
|
299
|
+
}
|
|
300
|
+
},
|
|
301
|
+
})
|
|
255
302
|
|
|
256
|
-
|
|
303
|
+
const restoreSpinner = createSpinner('Restoring backup...')
|
|
304
|
+
restoreSpinner.start()
|
|
257
305
|
|
|
258
|
-
|
|
259
|
-
|
|
306
|
+
const result = await engine.restore(config, backupPath, {
|
|
307
|
+
database: databaseName,
|
|
308
|
+
createDatabase: false,
|
|
309
|
+
})
|
|
260
310
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
311
|
+
// Check if restore completely failed (non-zero code with no data restored)
|
|
312
|
+
if (result.code !== 0 && result.stderr?.includes('FATAL')) {
|
|
313
|
+
restoreSpinner.fail('Restore failed')
|
|
314
|
+
throw new Error(result.stderr || 'Restore failed with fatal error')
|
|
315
|
+
}
|
|
265
316
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
317
|
+
if (result.code === 0) {
|
|
318
|
+
restoreSpinner.succeed('Backup restored successfully')
|
|
319
|
+
} else {
|
|
320
|
+
// pg_restore often returns warnings even on success
|
|
321
|
+
restoreSpinner.warn('Restore completed with warnings')
|
|
322
|
+
if (result.stderr) {
|
|
323
|
+
console.log(chalk.yellow('\n Warnings:'))
|
|
324
|
+
const lines = result.stderr.split('\n').slice(0, 5)
|
|
325
|
+
lines.forEach((line) => {
|
|
326
|
+
if (line.trim()) {
|
|
327
|
+
console.log(chalk.gray(` ${line}`))
|
|
328
|
+
}
|
|
329
|
+
})
|
|
330
|
+
if (result.stderr.split('\n').length > 5) {
|
|
331
|
+
console.log(chalk.gray(' ...'))
|
|
277
332
|
}
|
|
278
|
-
})
|
|
279
|
-
if (result.stderr.split('\n').length > 5) {
|
|
280
|
-
console.log(chalk.gray(' ...'))
|
|
281
333
|
}
|
|
282
334
|
}
|
|
335
|
+
|
|
336
|
+
// Restore succeeded - commit transaction (clear rollback actions)
|
|
337
|
+
tx.commit()
|
|
338
|
+
} catch (restoreErr) {
|
|
339
|
+
// Restore failed - execute rollbacks to clean up created database
|
|
340
|
+
if (databaseCreated) {
|
|
341
|
+
console.log(chalk.yellow('\n Cleaning up after failed restore...'))
|
|
342
|
+
await tx.rollback()
|
|
343
|
+
}
|
|
344
|
+
throw restoreErr
|
|
283
345
|
}
|
|
284
346
|
|
|
285
347
|
const connectionString = engine.getConnectionString(
|
|
@@ -287,7 +349,7 @@ export const restoreCommand = new Command('restore')
|
|
|
287
349
|
databaseName,
|
|
288
350
|
)
|
|
289
351
|
console.log()
|
|
290
|
-
console.log(
|
|
352
|
+
console.log(uiSuccess(`Database "${databaseName}" restored`))
|
|
291
353
|
console.log()
|
|
292
354
|
console.log(chalk.gray(' Connection string:'))
|
|
293
355
|
console.log(chalk.cyan(` ${connectionString}`))
|
|
@@ -305,8 +367,8 @@ export const restoreCommand = new Command('restore')
|
|
|
305
367
|
chalk.cyan(` spindb connect ${containerName} -d ${databaseName}`),
|
|
306
368
|
)
|
|
307
369
|
console.log()
|
|
308
|
-
} catch (
|
|
309
|
-
const e =
|
|
370
|
+
} catch (error) {
|
|
371
|
+
const e = error as Error
|
|
310
372
|
|
|
311
373
|
const missingToolPatterns = [
|
|
312
374
|
'pg_restore not found',
|
|
@@ -331,7 +393,7 @@ export const restoreCommand = new Command('restore')
|
|
|
331
393
|
process.exit(1)
|
|
332
394
|
}
|
|
333
395
|
|
|
334
|
-
console.error(
|
|
396
|
+
console.error(uiError(e.message))
|
|
335
397
|
process.exit(1)
|
|
336
398
|
} finally {
|
|
337
399
|
if (tempDumpPath) {
|
package/cli/commands/run.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { containerManager } from '../../core/container-manager'
|
|
|
5
5
|
import { processManager } from '../../core/process-manager'
|
|
6
6
|
import { getEngine } from '../../engines'
|
|
7
7
|
import { promptInstallDependencies } from '../ui/prompts'
|
|
8
|
-
import {
|
|
8
|
+
import { uiError, uiWarning } from '../ui/theme'
|
|
9
9
|
import { getMissingDependencies } from '../../core/dependency-manager'
|
|
10
10
|
import { Engine } from '../../types'
|
|
11
11
|
|
|
@@ -26,7 +26,7 @@ export const runCommand = new Command('run')
|
|
|
26
26
|
|
|
27
27
|
const config = await containerManager.getConfig(containerName)
|
|
28
28
|
if (!config) {
|
|
29
|
-
console.error(
|
|
29
|
+
console.error(uiError(`Container "${containerName}" not found`))
|
|
30
30
|
process.exit(1)
|
|
31
31
|
}
|
|
32
32
|
|
|
@@ -36,9 +36,7 @@ export const runCommand = new Command('run')
|
|
|
36
36
|
if (engineName === Engine.SQLite) {
|
|
37
37
|
if (!existsSync(config.database)) {
|
|
38
38
|
console.error(
|
|
39
|
-
|
|
40
|
-
`SQLite database file not found: ${config.database}`,
|
|
41
|
-
),
|
|
39
|
+
uiError(`SQLite database file not found: ${config.database}`),
|
|
42
40
|
)
|
|
43
41
|
process.exit(1)
|
|
44
42
|
}
|
|
@@ -49,7 +47,7 @@ export const runCommand = new Command('run')
|
|
|
49
47
|
})
|
|
50
48
|
if (!running) {
|
|
51
49
|
console.error(
|
|
52
|
-
|
|
50
|
+
uiError(
|
|
53
51
|
`Container "${containerName}" is not running. Start it first with: spindb start ${containerName}`,
|
|
54
52
|
),
|
|
55
53
|
)
|
|
@@ -59,16 +57,16 @@ export const runCommand = new Command('run')
|
|
|
59
57
|
|
|
60
58
|
if (file && options.sql) {
|
|
61
59
|
console.error(
|
|
62
|
-
|
|
60
|
+
uiError('Cannot specify both a file and --sql option. Choose one.'),
|
|
63
61
|
)
|
|
64
62
|
process.exit(1)
|
|
65
63
|
}
|
|
66
64
|
|
|
67
65
|
if (!file && !options.sql) {
|
|
68
|
-
console.error(
|
|
69
|
-
|
|
70
|
-
chalk.gray(' Usage: spindb run <container> <file.sql>'),
|
|
66
|
+
console.error(
|
|
67
|
+
uiError('Must provide either a SQL file or --sql option'),
|
|
71
68
|
)
|
|
69
|
+
console.log(chalk.gray(' Usage: spindb run <container> <file.sql>'))
|
|
72
70
|
console.log(
|
|
73
71
|
chalk.gray(' or: spindb run <container> --sql "SELECT ..."'),
|
|
74
72
|
)
|
|
@@ -76,7 +74,7 @@ export const runCommand = new Command('run')
|
|
|
76
74
|
}
|
|
77
75
|
|
|
78
76
|
if (file && !existsSync(file)) {
|
|
79
|
-
console.error(
|
|
77
|
+
console.error(uiError(`SQL file not found: ${file}`))
|
|
80
78
|
process.exit(1)
|
|
81
79
|
}
|
|
82
80
|
|
|
@@ -85,7 +83,7 @@ export const runCommand = new Command('run')
|
|
|
85
83
|
let missingDeps = await getMissingDependencies(engineName)
|
|
86
84
|
if (missingDeps.length > 0) {
|
|
87
85
|
console.log(
|
|
88
|
-
|
|
86
|
+
uiWarning(
|
|
89
87
|
`Missing tools: ${missingDeps.map((d) => d.name).join(', ')}`,
|
|
90
88
|
),
|
|
91
89
|
)
|
|
@@ -102,7 +100,7 @@ export const runCommand = new Command('run')
|
|
|
102
100
|
missingDeps = await getMissingDependencies(engineName)
|
|
103
101
|
if (missingDeps.length > 0) {
|
|
104
102
|
console.error(
|
|
105
|
-
|
|
103
|
+
uiError(
|
|
106
104
|
`Still missing tools: ${missingDeps.map((d) => d.name).join(', ')}`,
|
|
107
105
|
),
|
|
108
106
|
)
|
|
@@ -120,8 +118,8 @@ export const runCommand = new Command('run')
|
|
|
120
118
|
sql: options.sql,
|
|
121
119
|
database,
|
|
122
120
|
})
|
|
123
|
-
} catch (
|
|
124
|
-
const e =
|
|
121
|
+
} catch (error) {
|
|
122
|
+
const e = error as Error
|
|
125
123
|
|
|
126
124
|
const missingToolPatterns = [
|
|
127
125
|
'psql not found',
|
|
@@ -138,8 +136,12 @@ export const runCommand = new Command('run')
|
|
|
138
136
|
.replace(' not found', '')
|
|
139
137
|
.replace(' client', '')
|
|
140
138
|
// Determine engine from the missing tool name
|
|
141
|
-
const toolEngine =
|
|
142
|
-
|
|
139
|
+
const toolEngine =
|
|
140
|
+
missingTool === 'mysql' ? Engine.MySQL : Engine.PostgreSQL
|
|
141
|
+
const installed = await promptInstallDependencies(
|
|
142
|
+
missingTool,
|
|
143
|
+
toolEngine,
|
|
144
|
+
)
|
|
143
145
|
if (installed) {
|
|
144
146
|
console.log(
|
|
145
147
|
chalk.yellow(' Please re-run your command to continue.'),
|
|
@@ -148,7 +150,7 @@ export const runCommand = new Command('run')
|
|
|
148
150
|
process.exit(1)
|
|
149
151
|
}
|
|
150
152
|
|
|
151
|
-
console.error(
|
|
153
|
+
console.error(uiError(e.message))
|
|
152
154
|
process.exit(1)
|
|
153
155
|
}
|
|
154
156
|
},
|
|
@@ -3,7 +3,7 @@ import chalk from 'chalk'
|
|
|
3
3
|
import inquirer from 'inquirer'
|
|
4
4
|
import { updateManager } from '../../core/update-manager'
|
|
5
5
|
import { createSpinner } from '../ui/spinner'
|
|
6
|
-
import {
|
|
6
|
+
import { uiSuccess, uiError, uiInfo, header } from '../ui/theme'
|
|
7
7
|
|
|
8
8
|
export const selfUpdateCommand = new Command('self-update')
|
|
9
9
|
.alias('update')
|
|
@@ -24,7 +24,7 @@ export const selfUpdateCommand = new Command('self-update')
|
|
|
24
24
|
if (!result) {
|
|
25
25
|
checkSpinner.fail('Could not reach npm registry')
|
|
26
26
|
console.log()
|
|
27
|
-
console.log(
|
|
27
|
+
console.log(uiInfo('Check your internet connection and try again.'))
|
|
28
28
|
console.log(chalk.gray(' Manual update: npm install -g spindb@latest'))
|
|
29
29
|
process.exit(1)
|
|
30
30
|
}
|
|
@@ -84,7 +84,7 @@ export const selfUpdateCommand = new Command('self-update')
|
|
|
84
84
|
updateSpinner.succeed('Update complete')
|
|
85
85
|
console.log()
|
|
86
86
|
console.log(
|
|
87
|
-
|
|
87
|
+
uiSuccess(
|
|
88
88
|
`Updated from ${updateResult.previousVersion} to ${updateResult.newVersion}`,
|
|
89
89
|
),
|
|
90
90
|
)
|
|
@@ -100,9 +100,9 @@ export const selfUpdateCommand = new Command('self-update')
|
|
|
100
100
|
} else {
|
|
101
101
|
updateSpinner.fail('Update failed')
|
|
102
102
|
console.log()
|
|
103
|
-
console.log(
|
|
103
|
+
console.log(uiError(updateResult.error || 'Unknown error'))
|
|
104
104
|
console.log()
|
|
105
|
-
console.log(
|
|
105
|
+
console.log(uiInfo('Manual update: npm install -g spindb@latest'))
|
|
106
106
|
process.exit(1)
|
|
107
107
|
}
|
|
108
108
|
},
|
package/cli/commands/start.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { getEngine } from '../../engines'
|
|
|
7
7
|
import { getEngineDefaults } from '../../config/defaults'
|
|
8
8
|
import { promptContainerSelect } from '../ui/prompts'
|
|
9
9
|
import { createSpinner } from '../ui/spinner'
|
|
10
|
-
import {
|
|
10
|
+
import { uiError, uiWarning } from '../ui/theme'
|
|
11
11
|
|
|
12
12
|
export const startCommand = new Command('start')
|
|
13
13
|
.description('Start a container')
|
|
@@ -23,10 +23,10 @@ export const startCommand = new Command('start')
|
|
|
23
23
|
if (stopped.length === 0) {
|
|
24
24
|
if (containers.length === 0) {
|
|
25
25
|
console.log(
|
|
26
|
-
|
|
26
|
+
uiWarning('No containers found. Create one with: spindb create'),
|
|
27
27
|
)
|
|
28
28
|
} else {
|
|
29
|
-
console.log(
|
|
29
|
+
console.log(uiWarning('All containers are already running'))
|
|
30
30
|
}
|
|
31
31
|
return
|
|
32
32
|
}
|
|
@@ -41,7 +41,7 @@ export const startCommand = new Command('start')
|
|
|
41
41
|
|
|
42
42
|
const config = await containerManager.getConfig(containerName)
|
|
43
43
|
if (!config) {
|
|
44
|
-
console.error(
|
|
44
|
+
console.error(uiError(`Container "${containerName}" not found`))
|
|
45
45
|
process.exit(1)
|
|
46
46
|
}
|
|
47
47
|
|
|
@@ -51,7 +51,9 @@ export const startCommand = new Command('start')
|
|
|
51
51
|
engine: engineName,
|
|
52
52
|
})
|
|
53
53
|
if (running) {
|
|
54
|
-
console.log(
|
|
54
|
+
console.log(
|
|
55
|
+
uiWarning(`Container "${containerName}" is already running`),
|
|
56
|
+
)
|
|
55
57
|
return
|
|
56
58
|
}
|
|
57
59
|
|
|
@@ -72,7 +74,7 @@ export const startCommand = new Command('start')
|
|
|
72
74
|
if (!result.success) {
|
|
73
75
|
spinner.fail(`Failed to start "${containerName}"`)
|
|
74
76
|
if (result.error) {
|
|
75
|
-
console.error(
|
|
77
|
+
console.error(uiError(result.error.message))
|
|
76
78
|
}
|
|
77
79
|
process.exit(1)
|
|
78
80
|
}
|
|
@@ -110,9 +112,9 @@ export const startCommand = new Command('start')
|
|
|
110
112
|
console.log(chalk.gray(' Connect with:'))
|
|
111
113
|
console.log(chalk.cyan(` spindb connect ${containerName}`))
|
|
112
114
|
console.log()
|
|
113
|
-
} catch (
|
|
114
|
-
const e =
|
|
115
|
-
console.error(
|
|
115
|
+
} catch (error) {
|
|
116
|
+
const e = error as Error
|
|
117
|
+
console.error(uiError(e.message))
|
|
116
118
|
process.exit(1)
|
|
117
119
|
}
|
|
118
120
|
})
|