1package cli
2
3import (
4 "context"
5 "fmt"
6 "io"
7 "sort"
8
9 "github.com/bnema/gordon/internal/adapters/in/cli/remote"
10 "github.com/bnema/gordon/internal/adapters/in/cli/ui/components"
11 "github.com/spf13/cobra"
12)
13
14func newConfigCmd() *cobra.Command {
15 cmd := &cobra.Command{
16 Use: "config",
17 Short: "Inspect Gordon configuration",
18 }
19
20 cmd.AddCommand(newConfigShowCmd())
21
22 return cmd
23}
24
25func newConfigShowCmd() *cobra.Command {
26 var jsonOut bool
27
28 cmd := &cobra.Command{
29 Use: "show",
30 Short: "Show server configuration",
31 Long: `Display the full Gordon server configuration including server settings,
32auto-route, network isolation, routes, and external routes.
33
34Examples:
35 gordon config show
36 gordon config show --json
37 gordon config show --remote https://gordon.mydomain.com --token $TOKEN`,
38 RunE: func(cmd *cobra.Command, _ []string) error {
39 ctx := cmd.Context()
40 handle, err := resolveControlPlane(configPath)
41 if err != nil {
42 return err
43 }
44 defer handle.close()
45 return runConfigShow(ctx, handle.plane, cmd.OutOrStdout(), jsonOut)
46 },
47 }
48
49 cmd.Flags().BoolVar(&jsonOut, "json", false, "Output as JSON")
50
51 return cmd
52}
53
54func runConfigShow(ctx context.Context, cp ControlPlane, out io.Writer, jsonOut bool) error {
55 config, err := cp.GetConfig(ctx)
56 if err != nil {
57 return fmt.Errorf("failed to get config: %w", err)
58 }
59
60 if config == nil {
61 return fmt.Errorf("no config available")
62 }
63
64 if jsonOut {
65 return writeJSON(out, config)
66 }
67
68 return renderConfigTable(out, config)
69}
70
71func renderConfigTable(out io.Writer, config *remote.Config) error {
72 if err := renderConfigSummary(out, config); err != nil {
73 return err
74 }
75 if err := renderConfigRoutes(out, config); err != nil {
76 return err
77 }
78 return renderConfigExternalRoutes(out, config)
79}
80
81func renderConfigSummary(out io.Writer, config *remote.Config) error {
82 if err := cliWriteLine(out, cliRenderTitle("Gordon Configuration")); err != nil {
83 return err
84 }
85 if err := cliWriteLine(out, ""); err != nil {
86 return err
87 }
88 if err := cliWriteLine(out, cliRenderMeta("Server Port:", fmt.Sprintf("%d", config.Server.Port))); err != nil {
89 return err
90 }
91 if err := cliWriteLine(out, cliRenderMeta("Registry Port:", fmt.Sprintf("%d", config.Server.RegistryPort))); err != nil {
92 return err
93 }
94 if err := cliWriteLine(out, cliRenderMeta("Registry Domain:", config.Server.RegistryDomain)); err != nil {
95 return err
96 }
97 if err := cliWriteLine(out, cliRenderMeta("Data Directory:", config.Server.DataDir)); err != nil {
98 return err
99 }
100 if err := cliWriteLine(out, cliRenderMeta("Auto-Route:", fmt.Sprintf("%v", config.AutoRoute.Enabled))); err != nil {
101 return err
102 }
103 if err := cliWriteLine(out, cliRenderMeta("Network Isolation:", fmt.Sprintf("%v", config.NetworkIsolation.Enabled))); err != nil {
104 return err
105 }
106 if config.NetworkIsolation.Prefix != "" {
107 if err := cliWriteLine(out, cliRenderMeta("Network Prefix:", config.NetworkIsolation.Prefix)); err != nil {
108 return err
109 }
110 }
111 return nil
112}
113
114func renderConfigRoutes(out io.Writer, config *remote.Config) error {
115 if err := cliWriteLine(out, ""); err != nil {
116 return err
117 }
118 if len(config.Routes) == 0 {
119 if err := cliWriteLine(out, cliRenderMuted("No routes configured")); err != nil {
120 return err
121 }
122 } else {
123 if err := cliWriteLine(out, cliRenderTitle("Routes")); err != nil {
124 return err
125 }
126 rows := make([][]string, 0, len(config.Routes))
127 for _, route := range config.Routes {
128 rows = append(rows, []string{route.Domain, route.Image})
129 }
130 table := components.NewTable(
131 components.WithColumns([]components.TableColumn{
132 {Title: "Domain", Width: 30},
133 {Title: "Image", Width: 45},
134 }),
135 components.WithRows(rows),
136 )
137 if err := cliWriteLine(out, table.View()); err != nil {
138 return err
139 }
140 }
141 return nil
142}
143
144func renderConfigExternalRoutes(out io.Writer, config *remote.Config) error {
145 if err := cliWriteLine(out, ""); err != nil {
146 return err
147 }
148 if len(config.ExternalRoutes) == 0 {
149 if err := cliWriteLine(out, cliRenderTitle("External Routes")); err != nil {
150 return err
151 }
152 return cliWriteLine(out, cliRenderMuted("No external routes configured"))
153 }
154
155 if err := cliWriteLine(out, cliRenderTitle("External Routes")); err != nil {
156 return err
157 }
158 domains := make([]string, 0, len(config.ExternalRoutes))
159 for d := range config.ExternalRoutes {
160 domains = append(domains, d)
161 }
162 sort.Strings(domains)
163
164 rows := make([][]string, 0, len(domains))
165 for _, d := range domains {
166 rows = append(rows, []string{d, config.ExternalRoutes[d]})
167 }
168 table := components.NewTable(
169 components.WithColumns([]components.TableColumn{
170 {Title: "Domain", Width: 30},
171 {Title: "Target", Width: 45},
172 }),
173 components.WithRows(rows),
174 )
175 return cliWriteLine(out, table.View())
176}