sealos-cli 1.1.1 → 1.1.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 +10 -3
- package/dist/bin/cli.cjs +1347 -994
- package/dist/bin/cli.mjs +1347 -994
- package/dist/main.cjs +1347 -994
- package/dist/main.mjs +1347 -994
- package/package.json +1 -1
- package/src/commands/auth/index.ts +2 -2
- package/src/commands/auth/login.ts +1 -1
- package/src/commands/auth/logout.ts +21 -2
- package/src/commands/auth/whoami.ts +1 -1
- package/src/commands/database/index.ts +345 -30
- package/src/commands/devbox/index.ts +108 -24
- package/src/commands/template/index.ts +42 -14
- package/src/commands/workspace/index.ts +3 -3
- package/src/lib/auth.ts +17 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Command } from 'commander'
|
|
2
2
|
import chalk from 'chalk'
|
|
3
|
-
import { createDatabaseClient } from '../../lib/api-client.ts'
|
|
3
|
+
import { createDatabaseClient, resolveDbproviderHost } from '../../lib/api-client.ts'
|
|
4
|
+
import { DEFAULT_SEALOS_REGION, loadAuth } from '../../lib/auth.ts'
|
|
4
5
|
import { type ApiErrorBody, mapApiError } from '../../lib/errors.ts'
|
|
5
6
|
import { outputJson, outputTable } from '../../lib/output.ts'
|
|
6
7
|
import { withAuth, withErrorHandling } from '../../lib/with-auth.ts'
|
|
@@ -37,6 +38,46 @@ type DatabaseType = typeof SUPPORTED_DATABASE_TYPES[number]
|
|
|
37
38
|
type LogDbType = typeof SUPPORTED_LOG_DB_TYPES[number]
|
|
38
39
|
type LogType = typeof SUPPORTED_LOG_TYPES[number]
|
|
39
40
|
|
|
41
|
+
interface LegacyApiResponse<T> {
|
|
42
|
+
code: number
|
|
43
|
+
message?: string
|
|
44
|
+
data?: T
|
|
45
|
+
error?: unknown
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface SecretResponse {
|
|
49
|
+
username: string
|
|
50
|
+
password: string
|
|
51
|
+
host: string
|
|
52
|
+
port: string
|
|
53
|
+
connection: string
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
interface ClientAppConfigResponse {
|
|
57
|
+
domain?: string
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
interface ServiceResponse {
|
|
61
|
+
spec?: {
|
|
62
|
+
ports?: Array<{
|
|
63
|
+
nodePort?: number
|
|
64
|
+
port?: number
|
|
65
|
+
}>
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
interface ConnectionDetails {
|
|
70
|
+
privateConnection?: {
|
|
71
|
+
endpoint?: string
|
|
72
|
+
host?: string
|
|
73
|
+
port?: string
|
|
74
|
+
username?: string
|
|
75
|
+
password?: string
|
|
76
|
+
connectionString?: string
|
|
77
|
+
} | null
|
|
78
|
+
publicConnection?: string | null
|
|
79
|
+
}
|
|
80
|
+
|
|
40
81
|
export function collectOption (value: string, previous: string[]): string[] {
|
|
41
82
|
return [...previous, value]
|
|
42
83
|
}
|
|
@@ -70,6 +111,52 @@ export function normalizeDatabaseType (type: string): DatabaseType {
|
|
|
70
111
|
return resolved
|
|
71
112
|
}
|
|
72
113
|
|
|
114
|
+
function getDatabaseConnectScheme (type: string): string {
|
|
115
|
+
const databaseType = normalizeDatabaseType(type)
|
|
116
|
+
const schemes: Record<DatabaseType, string> = {
|
|
117
|
+
postgresql: 'postgresql',
|
|
118
|
+
mongodb: 'mongodb',
|
|
119
|
+
'apecloud-mysql': 'mysql',
|
|
120
|
+
mysql: 'mysql',
|
|
121
|
+
redis: 'redis',
|
|
122
|
+
kafka: 'kafka',
|
|
123
|
+
qdrant: 'qdrant',
|
|
124
|
+
nebula: 'nebula',
|
|
125
|
+
weaviate: 'weaviate',
|
|
126
|
+
milvus: 'milvus',
|
|
127
|
+
pulsar: 'pulsar',
|
|
128
|
+
clickhouse: 'clickhouse'
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return schemes[databaseType]
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function buildConsolePublicConnection (options: {
|
|
135
|
+
dbType: string
|
|
136
|
+
username?: string
|
|
137
|
+
password?: string
|
|
138
|
+
domain?: string
|
|
139
|
+
nodePort?: number | string | null
|
|
140
|
+
}): string | null {
|
|
141
|
+
if (!options.domain || !options.nodePort) return null
|
|
142
|
+
|
|
143
|
+
const scheme = getDatabaseConnectScheme(options.dbType)
|
|
144
|
+
const port = String(options.nodePort)
|
|
145
|
+
|
|
146
|
+
if (scheme === 'kafka' || scheme === 'milvus') {
|
|
147
|
+
return `${options.domain}:${port}`
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (!options.username || !options.password) return null
|
|
151
|
+
|
|
152
|
+
let connection = `${scheme}://${options.username}:${options.password}@${options.domain}:${port}`
|
|
153
|
+
if (scheme === 'mongodb' || scheme === 'postgresql') {
|
|
154
|
+
connection += '/?directConnection=true'
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return connection
|
|
158
|
+
}
|
|
159
|
+
|
|
73
160
|
export function normalizeLogDbType (type: string): LogDbType {
|
|
74
161
|
const normalized = normalizeDatabaseType(type)
|
|
75
162
|
if (!SUPPORTED_LOG_DB_TYPES.includes(normalized as LogDbType)) {
|
|
@@ -260,6 +347,103 @@ function printConnectionDetail (connection: any): void {
|
|
|
260
347
|
outputTable(rows)
|
|
261
348
|
}
|
|
262
349
|
|
|
350
|
+
function buildPublicAccessResult (action: 'enable-public' | 'disable-public', name: string, connection?: ConnectionDetails | null): Record<string, unknown> {
|
|
351
|
+
return {
|
|
352
|
+
success: true,
|
|
353
|
+
action,
|
|
354
|
+
resource: 'database',
|
|
355
|
+
name,
|
|
356
|
+
status: action === 'enable-public' ? 'enabled' : 'disabled',
|
|
357
|
+
publicConnection: connection?.publicConnection ?? null,
|
|
358
|
+
connection: connection ?? null
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function getDatabaseProviderHost (): string {
|
|
363
|
+
const override = process.env.SEALOS_DATABASE_HOST?.trim()
|
|
364
|
+
if (override) {
|
|
365
|
+
return override.replace(/\/+$/, '')
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
let authRegion: string | undefined
|
|
369
|
+
try {
|
|
370
|
+
authRegion = loadAuth().region
|
|
371
|
+
} catch {
|
|
372
|
+
authRegion = undefined
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
return resolveDbproviderHost(process.env.SEALOS_REGION || authRegion || DEFAULT_SEALOS_REGION)
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
async function getLegacyApiData<T> (path: string, headers: { Authorization: string }): Promise<T> {
|
|
379
|
+
const url = new URL(path, getDatabaseProviderHost())
|
|
380
|
+
const response = await fetch(url, {
|
|
381
|
+
headers: {
|
|
382
|
+
Authorization: headers.Authorization
|
|
383
|
+
}
|
|
384
|
+
})
|
|
385
|
+
const body = await response.json() as LegacyApiResponse<T>
|
|
386
|
+
|
|
387
|
+
if (!response.ok || body.code !== 200) {
|
|
388
|
+
throw new Error(body.message || `Request failed: ${url.pathname}`)
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
return body.data as T
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
async function fetchConsoleConnectionDetails (
|
|
395
|
+
name: string,
|
|
396
|
+
dbType: string,
|
|
397
|
+
fallbackConnection: ConnectionDetails | null | undefined,
|
|
398
|
+
headers: { Authorization: string }
|
|
399
|
+
): Promise<ConnectionDetails> {
|
|
400
|
+
const [secret, service, config] = await Promise.all([
|
|
401
|
+
getLegacyApiData<SecretResponse>(`/api/getSecretByName?dbName=${encodeURIComponent(name)}&dbType=${encodeURIComponent(dbType)}&mock=false`, headers),
|
|
402
|
+
getLegacyApiData<ServiceResponse>(`/api/getServiceByName?name=${encodeURIComponent(`${name}-export`)}`, headers).catch(() => null),
|
|
403
|
+
getLegacyApiData<ClientAppConfigResponse>('/api/platform/getClientAppConfig', headers).catch(() => null)
|
|
404
|
+
])
|
|
405
|
+
|
|
406
|
+
const nodePort = service?.spec?.ports?.find(port => port.nodePort)?.nodePort
|
|
407
|
+
const publicConnection = buildConsolePublicConnection({
|
|
408
|
+
dbType,
|
|
409
|
+
username: secret.username,
|
|
410
|
+
password: secret.password,
|
|
411
|
+
domain: config?.domain,
|
|
412
|
+
nodePort
|
|
413
|
+
}) ?? fallbackConnection?.publicConnection ?? null
|
|
414
|
+
|
|
415
|
+
return {
|
|
416
|
+
privateConnection: {
|
|
417
|
+
endpoint: `${secret.host}:${secret.port}`,
|
|
418
|
+
host: secret.host,
|
|
419
|
+
port: secret.port,
|
|
420
|
+
username: secret.username,
|
|
421
|
+
password: secret.password,
|
|
422
|
+
connectionString: secret.connection
|
|
423
|
+
},
|
|
424
|
+
publicConnection
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
async function loadDatabaseConnectionDetails (
|
|
429
|
+
name: string,
|
|
430
|
+
headers: { Authorization: string }
|
|
431
|
+
): Promise<ConnectionDetails | null> {
|
|
432
|
+
const client = createDatabaseClient()
|
|
433
|
+
const { data, error, response } = await client.GET('/databases/{databaseName}', {
|
|
434
|
+
headers,
|
|
435
|
+
params: {
|
|
436
|
+
path: { databaseName: name }
|
|
437
|
+
}
|
|
438
|
+
})
|
|
439
|
+
|
|
440
|
+
if (error) throw mapApiError(response.status, error as ApiErrorBody)
|
|
441
|
+
|
|
442
|
+
if (!data.type) return data.connection ?? null
|
|
443
|
+
|
|
444
|
+
return await fetchConsoleConnectionDetails(name, data.type, data.connection, headers)
|
|
445
|
+
}
|
|
446
|
+
|
|
263
447
|
export function createDatabaseCommand (): Command {
|
|
264
448
|
const dbCmd = new Command('database')
|
|
265
449
|
.alias('db')
|
|
@@ -268,7 +452,7 @@ export function createDatabaseCommand (): Command {
|
|
|
268
452
|
dbCmd
|
|
269
453
|
.command('list')
|
|
270
454
|
.description('List databases')
|
|
271
|
-
.option('-o, --output <format>', 'Output format (json|table)', '
|
|
455
|
+
.option('-o, --output <format>', 'Output format (json|table)', 'json')
|
|
272
456
|
.action(withAuth({ spinnerText: 'Loading databases...' }, async (ctx, options: { output: string }) => {
|
|
273
457
|
const client = createDatabaseClient()
|
|
274
458
|
const { data, error, response } = await client.GET('/databases', {
|
|
@@ -319,7 +503,7 @@ export function createDatabaseCommand (): Command {
|
|
|
319
503
|
dbCmd
|
|
320
504
|
.command('versions')
|
|
321
505
|
.description('List supported database versions (public endpoint)')
|
|
322
|
-
.option('-o, --output <format>', 'Output format (json|table)', '
|
|
506
|
+
.option('-o, --output <format>', 'Output format (json|table)', 'json')
|
|
323
507
|
.option('--host <host>', 'Sealos region host for public version lookup, e.g. https://gzg.sealos.run')
|
|
324
508
|
.option('--type <type>', 'Filter versions by database type')
|
|
325
509
|
.action(withErrorHandling({ spinnerText: 'Loading versions...' }, async (ctx, options: { output: string; host?: string; type?: string }) => {
|
|
@@ -384,7 +568,7 @@ export function createDatabaseCommand (): Command {
|
|
|
384
568
|
.option('--backup-save-time <count>', 'Retention count')
|
|
385
569
|
.option('--backup-save-type <type>', 'Retention unit (days|hours|weeks|months)')
|
|
386
570
|
.option('--param <KEY=VALUE>', 'Database parameter override', collectOption, [] as string[])
|
|
387
|
-
.option('-o, --output <format>', 'Output format (json|table)', '
|
|
571
|
+
.option('-o, --output <format>', 'Output format (json|table)', 'json')
|
|
388
572
|
.action(withAuth({
|
|
389
573
|
spinnerText: 'Creating database...'
|
|
390
574
|
}, async (
|
|
@@ -452,7 +636,7 @@ export function createDatabaseCommand (): Command {
|
|
|
452
636
|
.command('get <name>')
|
|
453
637
|
.alias('describe')
|
|
454
638
|
.description('Get database details')
|
|
455
|
-
.option('-o, --output <format>', 'Output format (json|table)', '
|
|
639
|
+
.option('-o, --output <format>', 'Output format (json|table)', 'json')
|
|
456
640
|
.action(withAuth({ spinnerText: 'Loading database...' }, async (ctx, name: string, options: { output: string }) => {
|
|
457
641
|
const client = createDatabaseClient()
|
|
458
642
|
const { data, error, response } = await client.GET('/databases/{databaseName}', {
|
|
@@ -477,26 +661,18 @@ export function createDatabaseCommand (): Command {
|
|
|
477
661
|
dbCmd
|
|
478
662
|
.command('connection <name>')
|
|
479
663
|
.description('Show database connection details')
|
|
480
|
-
.option('-o, --output <format>', 'Output format (json|table)', '
|
|
664
|
+
.option('-o, --output <format>', 'Output format (json|table)', 'json')
|
|
481
665
|
.action(withAuth({ spinnerText: 'Loading connection details...' }, async (ctx, name: string, options: { output: string }) => {
|
|
482
|
-
const
|
|
483
|
-
const { data, error, response } = await client.GET('/databases/{databaseName}', {
|
|
484
|
-
headers: ctx.auth,
|
|
485
|
-
params: {
|
|
486
|
-
path: { databaseName: name }
|
|
487
|
-
}
|
|
488
|
-
})
|
|
489
|
-
|
|
490
|
-
if (error) throw mapApiError(response.status, error as ApiErrorBody)
|
|
666
|
+
const connection = await loadDatabaseConnectionDetails(name, ctx.auth)
|
|
491
667
|
|
|
492
668
|
ctx.spinner.stop()
|
|
493
669
|
|
|
494
670
|
if (options.output === 'json') {
|
|
495
|
-
outputJson(
|
|
671
|
+
outputJson(connection)
|
|
496
672
|
return
|
|
497
673
|
}
|
|
498
674
|
|
|
499
|
-
printConnectionDetail(
|
|
675
|
+
printConnectionDetail(connection)
|
|
500
676
|
}))
|
|
501
677
|
|
|
502
678
|
dbCmd
|
|
@@ -506,12 +682,13 @@ export function createDatabaseCommand (): Command {
|
|
|
506
682
|
.option('--memory <memory>', 'Memory in GB per replica')
|
|
507
683
|
.option('--storage <storage>', 'Storage in GB per replica')
|
|
508
684
|
.option('--replicas <replicas>', 'Replica count')
|
|
685
|
+
.option('-o, --output <format>', 'Output format (json|table)', 'json')
|
|
509
686
|
.action(withAuth({
|
|
510
687
|
spinnerText: 'Updating database...'
|
|
511
688
|
}, async (
|
|
512
689
|
ctx,
|
|
513
690
|
name: string,
|
|
514
|
-
options: { cpu?: string; memory?: string; storage?: string; replicas?: string }
|
|
691
|
+
options: { cpu?: string; memory?: string; storage?: string; replicas?: string; output: string }
|
|
515
692
|
) => {
|
|
516
693
|
const quota = buildQuota(options)
|
|
517
694
|
if (Object.keys(quota).length === 0) {
|
|
@@ -529,13 +706,25 @@ export function createDatabaseCommand (): Command {
|
|
|
529
706
|
|
|
530
707
|
if (error) throw mapApiError(response.status, error as ApiErrorBody)
|
|
531
708
|
|
|
709
|
+
if (options.output === 'json') {
|
|
710
|
+
ctx.spinner.stop()
|
|
711
|
+
outputJson({
|
|
712
|
+
success: true,
|
|
713
|
+
action: 'update',
|
|
714
|
+
resource: 'database',
|
|
715
|
+
name,
|
|
716
|
+
status: 'requested'
|
|
717
|
+
})
|
|
718
|
+
return
|
|
719
|
+
}
|
|
532
720
|
ctx.spinner.succeed(`Database "${name}" update requested`)
|
|
533
721
|
}))
|
|
534
722
|
|
|
535
723
|
dbCmd
|
|
536
724
|
.command('start <name>')
|
|
537
725
|
.description('Start a database')
|
|
538
|
-
.
|
|
726
|
+
.option('-o, --output <format>', 'Output format (json|table)', 'json')
|
|
727
|
+
.action(withAuth({ spinnerText: 'Starting database...' }, async (ctx, name: string, options: { output: string }) => {
|
|
539
728
|
const client = createDatabaseClient()
|
|
540
729
|
const { error, response } = await client.POST('/databases/{databaseName}/start', {
|
|
541
730
|
headers: ctx.auth,
|
|
@@ -545,6 +734,17 @@ export function createDatabaseCommand (): Command {
|
|
|
545
734
|
})
|
|
546
735
|
|
|
547
736
|
if (error) throw mapApiError(response.status, error as ApiErrorBody)
|
|
737
|
+
if (options.output === 'json') {
|
|
738
|
+
ctx.spinner.stop()
|
|
739
|
+
outputJson({
|
|
740
|
+
success: true,
|
|
741
|
+
action: 'start',
|
|
742
|
+
resource: 'database',
|
|
743
|
+
name,
|
|
744
|
+
status: 'requested'
|
|
745
|
+
})
|
|
746
|
+
return
|
|
747
|
+
}
|
|
548
748
|
ctx.spinner.succeed(`Database "${name}" start requested`)
|
|
549
749
|
}))
|
|
550
750
|
|
|
@@ -552,7 +752,8 @@ export function createDatabaseCommand (): Command {
|
|
|
552
752
|
.command('pause <name>')
|
|
553
753
|
.alias('stop')
|
|
554
754
|
.description('Pause a database')
|
|
555
|
-
.
|
|
755
|
+
.option('-o, --output <format>', 'Output format (json|table)', 'json')
|
|
756
|
+
.action(withAuth({ spinnerText: 'Pausing database...' }, async (ctx, name: string, options: { output: string }) => {
|
|
556
757
|
const client = createDatabaseClient()
|
|
557
758
|
const { error, response } = await client.POST('/databases/{databaseName}/pause', {
|
|
558
759
|
headers: ctx.auth,
|
|
@@ -562,13 +763,25 @@ export function createDatabaseCommand (): Command {
|
|
|
562
763
|
})
|
|
563
764
|
|
|
564
765
|
if (error) throw mapApiError(response.status, error as ApiErrorBody)
|
|
766
|
+
if (options.output === 'json') {
|
|
767
|
+
ctx.spinner.stop()
|
|
768
|
+
outputJson({
|
|
769
|
+
success: true,
|
|
770
|
+
action: 'pause',
|
|
771
|
+
resource: 'database',
|
|
772
|
+
name,
|
|
773
|
+
status: 'requested'
|
|
774
|
+
})
|
|
775
|
+
return
|
|
776
|
+
}
|
|
565
777
|
ctx.spinner.succeed(`Database "${name}" pause requested`)
|
|
566
778
|
}))
|
|
567
779
|
|
|
568
780
|
dbCmd
|
|
569
781
|
.command('restart <name>')
|
|
570
782
|
.description('Restart a database')
|
|
571
|
-
.
|
|
783
|
+
.option('-o, --output <format>', 'Output format (json|table)', 'json')
|
|
784
|
+
.action(withAuth({ spinnerText: 'Restarting database...' }, async (ctx, name: string, options: { output: string }) => {
|
|
572
785
|
const client = createDatabaseClient()
|
|
573
786
|
const { error, response } = await client.POST('/databases/{databaseName}/restart', {
|
|
574
787
|
headers: ctx.auth,
|
|
@@ -578,6 +791,17 @@ export function createDatabaseCommand (): Command {
|
|
|
578
791
|
})
|
|
579
792
|
|
|
580
793
|
if (error) throw mapApiError(response.status, error as ApiErrorBody)
|
|
794
|
+
if (options.output === 'json') {
|
|
795
|
+
ctx.spinner.stop()
|
|
796
|
+
outputJson({
|
|
797
|
+
success: true,
|
|
798
|
+
action: 'restart',
|
|
799
|
+
resource: 'database',
|
|
800
|
+
name,
|
|
801
|
+
status: 'requested'
|
|
802
|
+
})
|
|
803
|
+
return
|
|
804
|
+
}
|
|
581
805
|
ctx.spinner.succeed(`Database "${name}" restart requested`)
|
|
582
806
|
}))
|
|
583
807
|
|
|
@@ -585,7 +809,8 @@ export function createDatabaseCommand (): Command {
|
|
|
585
809
|
.command('delete <name>')
|
|
586
810
|
.description('Delete a database')
|
|
587
811
|
.option('-f, --force', 'Delete without confirmation')
|
|
588
|
-
.
|
|
812
|
+
.option('-o, --output <format>', 'Output format (json|table)', 'json')
|
|
813
|
+
.action(withAuth({ spinnerText: 'Deleting database...' }, async (ctx, name: string, options: { output: string }) => {
|
|
589
814
|
const client = createDatabaseClient()
|
|
590
815
|
const { error, response } = await client.DELETE('/databases/{databaseName}', {
|
|
591
816
|
headers: ctx.auth,
|
|
@@ -595,13 +820,24 @@ export function createDatabaseCommand (): Command {
|
|
|
595
820
|
})
|
|
596
821
|
|
|
597
822
|
if (error) throw mapApiError(response.status, error as ApiErrorBody)
|
|
823
|
+
if (options.output === 'json') {
|
|
824
|
+
ctx.spinner.stop()
|
|
825
|
+
outputJson({
|
|
826
|
+
success: true,
|
|
827
|
+
action: 'delete',
|
|
828
|
+
resource: 'database',
|
|
829
|
+
name,
|
|
830
|
+
status: 'requested'
|
|
831
|
+
})
|
|
832
|
+
return
|
|
833
|
+
}
|
|
598
834
|
ctx.spinner.succeed(`Database "${name}" delete requested`)
|
|
599
835
|
}))
|
|
600
836
|
|
|
601
837
|
dbCmd
|
|
602
838
|
.command('backups <name>')
|
|
603
839
|
.description('List backups for a database')
|
|
604
|
-
.option('-o, --output <format>', 'Output format (json|table)', '
|
|
840
|
+
.option('-o, --output <format>', 'Output format (json|table)', 'json')
|
|
605
841
|
.action(withAuth({ spinnerText: 'Loading backups...' }, async (ctx, name: string, options: { output: string }) => {
|
|
606
842
|
const client = createDatabaseClient()
|
|
607
843
|
const { data, error, response } = await client.GET('/databases/{databaseName}/backups', {
|
|
@@ -649,12 +885,13 @@ export function createDatabaseCommand (): Command {
|
|
|
649
885
|
.description('Create a database backup')
|
|
650
886
|
.option('--name <backupName>', 'Backup name')
|
|
651
887
|
.option('--description <description>', 'Backup description')
|
|
888
|
+
.option('-o, --output <format>', 'Output format (json|table)', 'json')
|
|
652
889
|
.action(withAuth({
|
|
653
890
|
spinnerText: 'Creating backup...'
|
|
654
891
|
}, async (
|
|
655
892
|
ctx,
|
|
656
893
|
name: string,
|
|
657
|
-
options: { name?: string; description?: string }
|
|
894
|
+
options: { name?: string; description?: string; output: string }
|
|
658
895
|
) => {
|
|
659
896
|
const client = createDatabaseClient()
|
|
660
897
|
const body: Record<string, string> = {}
|
|
@@ -670,15 +907,28 @@ export function createDatabaseCommand (): Command {
|
|
|
670
907
|
})
|
|
671
908
|
|
|
672
909
|
if (error) throw mapApiError(response.status, error as ApiErrorBody)
|
|
910
|
+
if (options.output === 'json') {
|
|
911
|
+
ctx.spinner.stop()
|
|
912
|
+
outputJson({
|
|
913
|
+
success: true,
|
|
914
|
+
action: 'backup',
|
|
915
|
+
resource: 'database',
|
|
916
|
+
name,
|
|
917
|
+
backupName: options.name ?? null,
|
|
918
|
+
status: 'requested'
|
|
919
|
+
})
|
|
920
|
+
return
|
|
921
|
+
}
|
|
673
922
|
ctx.spinner.succeed(`Backup requested for database "${name}"`)
|
|
674
923
|
}))
|
|
675
924
|
|
|
676
925
|
dbCmd
|
|
677
926
|
.command('backup-delete <databaseName> <backupName>')
|
|
678
927
|
.description('Delete a database backup')
|
|
928
|
+
.option('-o, --output <format>', 'Output format (json|table)', 'json')
|
|
679
929
|
.action(withAuth({
|
|
680
930
|
spinnerText: 'Deleting backup...'
|
|
681
|
-
}, async (ctx, databaseName: string, backupName: string) => {
|
|
931
|
+
}, async (ctx, databaseName: string, backupName: string, options: { output: string }) => {
|
|
682
932
|
const client = createDatabaseClient()
|
|
683
933
|
const { error, response } = await client.DELETE('/databases/{databaseName}/backups/{backupName}', {
|
|
684
934
|
headers: ctx.auth,
|
|
@@ -688,6 +938,18 @@ export function createDatabaseCommand (): Command {
|
|
|
688
938
|
})
|
|
689
939
|
|
|
690
940
|
if (error) throw mapApiError(response.status, error as ApiErrorBody)
|
|
941
|
+
if (options.output === 'json') {
|
|
942
|
+
ctx.spinner.stop()
|
|
943
|
+
outputJson({
|
|
944
|
+
success: true,
|
|
945
|
+
action: 'backup-delete',
|
|
946
|
+
resource: 'database-backup',
|
|
947
|
+
databaseName,
|
|
948
|
+
backupName,
|
|
949
|
+
status: 'deleted'
|
|
950
|
+
})
|
|
951
|
+
return
|
|
952
|
+
}
|
|
691
953
|
ctx.spinner.succeed(`Backup "${backupName}" deleted`)
|
|
692
954
|
}))
|
|
693
955
|
|
|
@@ -697,12 +959,13 @@ export function createDatabaseCommand (): Command {
|
|
|
697
959
|
.requiredOption('--from <backupName>', 'Backup name to restore from')
|
|
698
960
|
.option('--name <name>', 'Name for the restored database')
|
|
699
961
|
.option('--replicas <replicas>', 'Replica count for the restored database')
|
|
962
|
+
.option('-o, --output <format>', 'Output format (json|table)', 'json')
|
|
700
963
|
.action(withAuth({
|
|
701
964
|
spinnerText: 'Restoring database...'
|
|
702
965
|
}, async (
|
|
703
966
|
ctx,
|
|
704
967
|
databaseName: string,
|
|
705
|
-
options: { from: string; name?: string; replicas?: string }
|
|
968
|
+
options: { from: string; name?: string; replicas?: string; output: string }
|
|
706
969
|
) => {
|
|
707
970
|
const client = createDatabaseClient()
|
|
708
971
|
const body: Record<string, unknown> = {}
|
|
@@ -718,13 +981,28 @@ export function createDatabaseCommand (): Command {
|
|
|
718
981
|
})
|
|
719
982
|
|
|
720
983
|
if (error) throw mapApiError(response.status, error as ApiErrorBody)
|
|
984
|
+
if (options.output === 'json') {
|
|
985
|
+
ctx.spinner.stop()
|
|
986
|
+
outputJson({
|
|
987
|
+
success: true,
|
|
988
|
+
action: 'restore',
|
|
989
|
+
resource: 'database',
|
|
990
|
+
databaseName,
|
|
991
|
+
backupName: options.from,
|
|
992
|
+
restoredName: options.name ?? null,
|
|
993
|
+
status: 'requested'
|
|
994
|
+
})
|
|
995
|
+
return
|
|
996
|
+
}
|
|
721
997
|
ctx.spinner.succeed(`Restore requested from backup "${options.from}"`)
|
|
722
998
|
}))
|
|
723
999
|
|
|
724
1000
|
dbCmd
|
|
725
1001
|
.command('enable-public <name>')
|
|
1002
|
+
.alias('expose')
|
|
726
1003
|
.description('Enable public access for a database')
|
|
727
|
-
.
|
|
1004
|
+
.option('-o, --output <format>', 'Output format (json|table)', 'json')
|
|
1005
|
+
.action(withAuth({ spinnerText: 'Enabling public access...' }, async (ctx, name: string, options: { output: string }) => {
|
|
728
1006
|
const client = createDatabaseClient()
|
|
729
1007
|
const { error, response } = await client.POST('/databases/{databaseName}/enable-public', {
|
|
730
1008
|
headers: ctx.auth,
|
|
@@ -734,13 +1012,23 @@ export function createDatabaseCommand (): Command {
|
|
|
734
1012
|
})
|
|
735
1013
|
|
|
736
1014
|
if (error) throw mapApiError(response.status, error as ApiErrorBody)
|
|
1015
|
+
const connection = await loadDatabaseConnectionDetails(name, ctx.auth)
|
|
1016
|
+
|
|
1017
|
+
if (options.output === 'json') {
|
|
1018
|
+
ctx.spinner.stop()
|
|
1019
|
+
outputJson(buildPublicAccessResult('enable-public', name, connection))
|
|
1020
|
+
return
|
|
1021
|
+
}
|
|
737
1022
|
ctx.spinner.succeed(`Public access enabled for "${name}"`)
|
|
1023
|
+
printConnectionDetail(connection)
|
|
738
1024
|
}))
|
|
739
1025
|
|
|
740
1026
|
dbCmd
|
|
741
1027
|
.command('disable-public <name>')
|
|
1028
|
+
.alias('unexpose')
|
|
742
1029
|
.description('Disable public access for a database')
|
|
743
|
-
.
|
|
1030
|
+
.option('-o, --output <format>', 'Output format (json|table)', 'json')
|
|
1031
|
+
.action(withAuth({ spinnerText: 'Disabling public access...' }, async (ctx, name: string, options: { output: string }) => {
|
|
744
1032
|
const client = createDatabaseClient()
|
|
745
1033
|
const { error, response } = await client.POST('/databases/{databaseName}/disable-public', {
|
|
746
1034
|
headers: ctx.auth,
|
|
@@ -750,6 +1038,11 @@ export function createDatabaseCommand (): Command {
|
|
|
750
1038
|
})
|
|
751
1039
|
|
|
752
1040
|
if (error) throw mapApiError(response.status, error as ApiErrorBody)
|
|
1041
|
+
if (options.output === 'json') {
|
|
1042
|
+
ctx.spinner.stop()
|
|
1043
|
+
outputJson(buildPublicAccessResult('disable-public', name))
|
|
1044
|
+
return
|
|
1045
|
+
}
|
|
753
1046
|
ctx.spinner.succeed(`Public access disabled for "${name}"`)
|
|
754
1047
|
}))
|
|
755
1048
|
|
|
@@ -761,7 +1054,7 @@ export function createDatabaseCommand (): Command {
|
|
|
761
1054
|
.requiredOption('--log-path <path>', 'Log path to read. Use "log-files" first to discover valid paths')
|
|
762
1055
|
.option('--page <page>', 'Page number', '1')
|
|
763
1056
|
.option('--page-size <pageSize>', 'Page size', '200')
|
|
764
|
-
.option('-o, --output <format>', 'Output format (
|
|
1057
|
+
.option('-o, --output <format>', 'Output format (json|table|plain)', 'json')
|
|
765
1058
|
.action(withAuth({
|
|
766
1059
|
spinnerText: 'Loading logs...'
|
|
767
1060
|
}, async (
|
|
@@ -820,7 +1113,7 @@ export function createDatabaseCommand (): Command {
|
|
|
820
1113
|
.description('List database log files for a pod')
|
|
821
1114
|
.requiredOption('--db-type <type>', 'Database type used by the log service')
|
|
822
1115
|
.requiredOption('--log-type <type>', 'Log type used by the log service')
|
|
823
|
-
.option('-o, --output <format>', 'Output format (json|table)', '
|
|
1116
|
+
.option('-o, --output <format>', 'Output format (json|table)', 'json')
|
|
824
1117
|
.action(withAuth({
|
|
825
1118
|
spinnerText: 'Loading log files...'
|
|
826
1119
|
}, async (
|
|
@@ -877,5 +1170,27 @@ export function createDatabaseCommand (): Command {
|
|
|
877
1170
|
outputTable(rows)
|
|
878
1171
|
}))
|
|
879
1172
|
|
|
1173
|
+
dbCmd
|
|
1174
|
+
.command('* [args...]', { hidden: true })
|
|
1175
|
+
.option('-o, --output <format>', 'Output format (json|table)', 'json')
|
|
1176
|
+
.allowUnknownOption()
|
|
1177
|
+
.action(async (args: string[], options: { output: string }) => {
|
|
1178
|
+
const [name, operation, ...rest] = args
|
|
1179
|
+
const aliases: Record<string, string> = {
|
|
1180
|
+
connection: 'connection',
|
|
1181
|
+
connect: 'connection',
|
|
1182
|
+
'enable-public': 'enable-public',
|
|
1183
|
+
expose: 'expose',
|
|
1184
|
+
'disable-public': 'disable-public',
|
|
1185
|
+
unexpose: 'unexpose'
|
|
1186
|
+
}
|
|
1187
|
+
const command = operation ? aliases[operation] : undefined
|
|
1188
|
+
if (!name || !command) {
|
|
1189
|
+
throw new Error('Unknown database command. Use "sealos-cli database --help" to list supported commands.')
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
await dbCmd.parseAsync([command, name, ...rest, '--output', options.output], { from: 'user' })
|
|
1193
|
+
})
|
|
1194
|
+
|
|
880
1195
|
return dbCmd
|
|
881
1196
|
}
|