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.
@@ -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)', '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)', '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)', '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)', '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)', '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 client = createDatabaseClient()
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(data.connection ?? null)
671
+ outputJson(connection)
496
672
  return
497
673
  }
498
674
 
499
- printConnectionDetail(data.connection)
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
- .action(withAuth({ spinnerText: 'Starting database...' }, async (ctx, name: string) => {
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
- .action(withAuth({ spinnerText: 'Pausing database...' }, async (ctx, name: string) => {
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
- .action(withAuth({ spinnerText: 'Restarting database...' }, async (ctx, name: string) => {
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
- .action(withAuth({ spinnerText: 'Deleting database...' }, async (ctx, name: string) => {
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)', '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
- .action(withAuth({ spinnerText: 'Enabling public access...' }, async (ctx, name: string) => {
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
- .action(withAuth({ spinnerText: 'Disabling public access...' }, async (ctx, name: string) => {
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 (plain|json|table)', 'plain')
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)', '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
  }