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 config.Server.DataDir != "" {
98 if err := cliWriteLine(out, cliRenderMeta("Data Directory:", config.Server.DataDir)); err != nil {
99 return err
100 }
101 }
102 if err := cliWriteLine(out, cliRenderMeta("Auto-Route:", fmt.Sprintf("%v", config.AutoRoute.Enabled))); err != nil {
103 return err
104 }
105 if err := cliWriteLine(out, cliRenderMeta("Network Isolation:", fmt.Sprintf("%v", config.NetworkIsolation.Enabled))); err != nil {
106 return err
107 }
108 if config.NetworkIsolation.Prefix != "" {
109 if err := cliWriteLine(out, cliRenderMeta("Network Prefix:", config.NetworkIsolation.Prefix)); err != nil {
110 return err
111 }
112 }
113 return nil
114}
115
116func renderConfigRoutes(out io.Writer, config *remote.Config) error {
117 if err := cliWriteLine(out, ""); err != nil {
118 return err
119 }
120 if len(config.Routes) == 0 {
121 if err := cliWriteLine(out, cliRenderMuted("No routes configured")); err != nil {
122 return err
123 }
124 } else {
125 if err := cliWriteLine(out, cliRenderTitle("Routes")); err != nil {
126 return err
127 }
128 rows := make([][]string, 0, len(config.Routes))
129 for _, route := range config.Routes {
130 rows = append(rows, []string{route.Domain, route.Image})
131 }
132 table := components.NewTable(
133 components.WithColumns([]components.TableColumn{
134 {Title: "Domain", Width: 30},
135 {Title: "Image", Width: 45},
136 }),
137 components.WithRows(rows),
138 )
139 if err := cliWriteLine(out, table.View()); err != nil {
140 return err
141 }
142 }
143 return nil
144}
145
146func renderConfigExternalRoutes(out io.Writer, config *remote.Config) error {
147 if err := cliWriteLine(out, ""); err != nil {
148 return err
149 }
150 if len(config.ExternalRoutes) == 0 {
151 if err := cliWriteLine(out, cliRenderTitle("External Routes")); err != nil {
152 return err
153 }
154 return cliWriteLine(out, cliRenderMuted("No external routes configured"))
155 }
156
157 if err := cliWriteLine(out, cliRenderTitle("External Routes")); err != nil {
158 return err
159 }
160 routes := append([]remote.ExternalRoute(nil), config.ExternalRoutes...)
161 sort.Slice(routes, func(i, j int) bool { return routes[i].Domain < routes[j].Domain })
162
163 rows := make([][]string, 0, len(routes))
164 for _, route := range routes {
165 target := route.Target
166 if target == "" {
167 target = "[redacted]"
168 }
169 rows = append(rows, []string{route.Domain, target})
170 }
171 table := components.NewTable(
172 components.WithColumns([]components.TableColumn{
173 {Title: "Domain", Width: 30},
174 {Title: "Target", Width: 45},
175 }),
176 components.WithRows(rows),
177 )
178 return cliWriteLine(out, table.View())
179}