feat(cli): add comprehensive command suite
+ Adds new commands for managing boxes, environment variables, and running the server. * The command structure is greatly expanded to improve user experience and coverage for core service functionalities.
This commit is contained in:
276
cmd/cmd_env.go
Normal file
276
cmd/cmd_env.go
Normal file
@@ -0,0 +1,276 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"warpbox/lib/config"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func newEnvCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "env",
|
||||
Short: "Explore environment variable options",
|
||||
Long: "List and inspect WarpBox environment variables sourced from the codebase.",
|
||||
}
|
||||
|
||||
cmd.AddCommand(newEnvListCommand())
|
||||
cmd.AddCommand(newEnvDescribeCommand())
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newEnvListCommand() *cobra.Command {
|
||||
var format string
|
||||
var includeHidden bool
|
||||
cmd := &cobra.Command{
|
||||
Use: "ls",
|
||||
Aliases: []string{"list"},
|
||||
Short: "List all environment variables",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return formatEnvList(format, includeHidden)
|
||||
},
|
||||
}
|
||||
cmd.Flags().StringVarP(&format, "format", "o", "table", "Output format: table, json, env")
|
||||
cmd.Flags().BoolVar(&includeHidden, "hidden", false, "Include non-editable and hard-limit settings")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func newEnvDescribeCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "describe",
|
||||
Aliases: []string{"show", "info", "get"},
|
||||
Short: "Describe an environment variable",
|
||||
Long: "Show detailed info about a specific env var or setting key.",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return describeEnvVar(args[0])
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
type envRow struct {
|
||||
EnvName string
|
||||
Key string
|
||||
Label string
|
||||
Type config.SettingType
|
||||
Default string
|
||||
Editable bool
|
||||
HardLimit bool
|
||||
Minimum int64
|
||||
}
|
||||
|
||||
type describeRow struct {
|
||||
EnvName string
|
||||
Key string
|
||||
Label string
|
||||
Type config.SettingType
|
||||
Default string
|
||||
Value string
|
||||
Source string
|
||||
Editable bool
|
||||
HardLimit bool
|
||||
Minimum int64
|
||||
}
|
||||
|
||||
func formatEnvList(format string, includeHidden bool) error {
|
||||
allRows := buildAllEnvRows(includeHidden)
|
||||
|
||||
switch format {
|
||||
case "json":
|
||||
type envOut struct {
|
||||
EnvName string `json:"env_name"`
|
||||
Key string `json:"key"`
|
||||
Label string `json:"label"`
|
||||
Type string `json:"type"`
|
||||
Default string `json:"default"`
|
||||
Editable bool `json:"editable"`
|
||||
HardLimit bool `json:"hard_limit"`
|
||||
Minimum int64 `json:"minimum,omitempty"`
|
||||
}
|
||||
out := make([]envOut, len(allRows))
|
||||
for i, r := range allRows {
|
||||
out[i] = envOut{
|
||||
EnvName: r.EnvName, Key: r.Key, Label: r.Label,
|
||||
Type: string(r.Type), Default: r.Default, Editable: r.Editable,
|
||||
HardLimit: r.HardLimit, Minimum: r.Minimum,
|
||||
}
|
||||
}
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
enc.SetIndent("", " ")
|
||||
return enc.Encode(out)
|
||||
|
||||
case "env":
|
||||
for _, r := range allRows {
|
||||
fmt.Printf("%s=\"%s\"\n", r.EnvName, r.Default)
|
||||
}
|
||||
return nil
|
||||
|
||||
case "table", "":
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
fmt.Fprintln(w, "ENV NAME\tKey\tLabel\tType\tDefault\tEditable")
|
||||
for _, r := range allRows {
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%v\n",
|
||||
r.EnvName, r.Key, r.Label, r.Type, r.Default, r.Editable)
|
||||
}
|
||||
return w.Flush()
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unknown format: %s (use 'table', 'json', or 'env')", format)
|
||||
}
|
||||
}
|
||||
|
||||
func buildAllEnvRows(includeHidden bool) []envRow {
|
||||
cfg, loadErr := config.Load()
|
||||
|
||||
var rows []envRow
|
||||
|
||||
for _, def := range config.Definitions {
|
||||
if !includeHidden && (!def.Editable || def.HardLimit) {
|
||||
continue
|
||||
}
|
||||
row := envRow{
|
||||
EnvName: def.EnvName,
|
||||
Key: def.Key,
|
||||
Label: def.Label,
|
||||
Type: def.Type,
|
||||
Editable: def.Editable,
|
||||
HardLimit: def.HardLimit,
|
||||
Minimum: def.Minimum,
|
||||
}
|
||||
if loadErr == nil {
|
||||
row.Default = getEnvDefault(cfg, def)
|
||||
}
|
||||
rows = append(rows, row)
|
||||
}
|
||||
|
||||
extra := buildExtraEnvRows(includeHidden)
|
||||
if loadErr == nil {
|
||||
for i := range extra {
|
||||
extra[i].Default = extra[i].Default
|
||||
}
|
||||
}
|
||||
rows = append(rows, extra...)
|
||||
|
||||
return rows
|
||||
}
|
||||
|
||||
func getEnvDefault(cfg *config.Config, def config.SettingDefinition) string {
|
||||
for _, row := range cfg.SettingRows() {
|
||||
if row.Definition.Key == def.Key && row.Source == config.SourceDefault {
|
||||
return row.Value
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func buildExtraEnvRows(includeHidden bool) []envRow {
|
||||
extra := []envRow{
|
||||
{EnvName: "WARPBOX_ADMIN_ENABLED", Key: "admin_enabled", Label: "Admin interface mode", Type: config.SettingTypeText, Editable: false, Default: "auto"},
|
||||
{EnvName: "WARPBOX_ADMIN_USERNAME", Key: "admin_username", Label: "Admin username", Type: config.SettingTypeText, Editable: false, Default: "admin"},
|
||||
{EnvName: "WARPBOX_ADMIN_PASSWORD", Key: "admin_password", Label: "Admin password", Type: config.SettingTypeText, Editable: false, Default: "(none)"},
|
||||
{EnvName: "WARPBOX_ADMIN_EMAIL", Key: "admin_email", Label: "Admin email", Type: config.SettingTypeText, Editable: false, Default: "(none)"},
|
||||
{EnvName: "WARPBOX_ADMIN_COOKIE_SECURE", Key: "admin_cookie_secure", Label: "Admin cookie secure flag", Type: config.SettingTypeBool, Editable: false, Default: "false"},
|
||||
{EnvName: "WARPBOX_ALLOW_ADMIN_SETTINGS_OVERRIDE", Key: "allow_admin_override", Label: "Allow admin UI to override settings", Type: config.SettingTypeBool, Editable: false, HardLimit: true, Default: "true"},
|
||||
}
|
||||
|
||||
sizePairs := []struct {
|
||||
bytesEnv string
|
||||
mbEnv string
|
||||
label string
|
||||
}{
|
||||
{"WARPBOX_GLOBAL_MAX_FILE_SIZE_BYTES", "WARPBOX_GLOBAL_MAX_FILE_SIZE_MB", "Global max file size"},
|
||||
{"WARPBOX_GLOBAL_MAX_BOX_SIZE_BYTES", "WARPBOX_GLOBAL_MAX_BOX_SIZE_MB", "Global max box size"},
|
||||
{"WARPBOX_DEFAULT_USER_MAX_FILE_SIZE_BYTES", "WARPBOX_DEFAULT_USER_MAX_FILE_SIZE_MB", "Default user max file size"},
|
||||
{"WARPBOX_DEFAULT_USER_MAX_BOX_SIZE_BYTES", "WARPBOX_DEFAULT_USER_MAX_BOX_SIZE_MB", "Default user max box size"},
|
||||
}
|
||||
|
||||
for _, pair := range sizePairs {
|
||||
extra = append(extra, envRow{EnvName: pair.bytesEnv, Key: pair.bytesEnv, Label: pair.label + " (bytes)", Type: config.SettingTypeInt64, Editable: false, HardLimit: true, Minimum: 0, Default: "(use bytes or MB variant)"})
|
||||
extra = append(extra, envRow{EnvName: pair.mbEnv, Key: pair.mbEnv, Label: pair.label + " (MB)", Type: config.SettingTypeInt64, Editable: false, HardLimit: true, Minimum: 0, Default: "(use bytes or MB variant)"})
|
||||
}
|
||||
|
||||
return extra
|
||||
}
|
||||
|
||||
func describeEnvVar(query string) error {
|
||||
cfg, loadErr := config.Load()
|
||||
|
||||
for _, def := range config.Definitions {
|
||||
if matchEnv(query, def.EnvName, def.Key) {
|
||||
row := describeRow{
|
||||
EnvName: def.EnvName,
|
||||
Key: def.Key,
|
||||
Label: def.Label,
|
||||
Type: def.Type,
|
||||
Editable: def.Editable,
|
||||
HardLimit: def.HardLimit,
|
||||
Minimum: def.Minimum,
|
||||
}
|
||||
if loadErr == nil {
|
||||
for _, r := range cfg.SettingRows() {
|
||||
if r.Definition.Key == def.Key {
|
||||
row.Value = r.Value
|
||||
row.Source = string(r.Source)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
printDescribeRow(row)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
extras := buildExtraEnvRows(true)
|
||||
for _, er := range extras {
|
||||
if matchEnv(query, er.EnvName, er.Key) {
|
||||
row := describeRow{
|
||||
EnvName: er.EnvName,
|
||||
Key: er.Key,
|
||||
Label: er.Label,
|
||||
Type: er.Type,
|
||||
Editable: er.Editable,
|
||||
HardLimit: er.HardLimit,
|
||||
Minimum: er.Minimum,
|
||||
Default: er.Default,
|
||||
}
|
||||
printDescribeRow(row)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("no environment variable found matching: %s\n\nUse 'warpbox env ls' to list all available options.", query)
|
||||
}
|
||||
|
||||
func matchEnv(query, envName, key string) bool {
|
||||
return strings.EqualFold(query, envName) || strings.EqualFold(query, key)
|
||||
}
|
||||
|
||||
func printDescribeRow(r describeRow) {
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
fmt.Fprintf(w, "Environment Variable:\t%s\n", r.EnvName)
|
||||
fmt.Fprintf(w, "Setting Key:\t%s\n", r.Key)
|
||||
fmt.Fprintf(w, "Label:\t%s\n", r.Label)
|
||||
fmt.Fprintf(w, "Type:\t%s\n", r.Type)
|
||||
fmt.Fprintf(w, "Editable (runtime):\t%v\n", r.Editable)
|
||||
fmt.Fprintf(w, "Hard Limit:\t%v\n", r.HardLimit)
|
||||
if r.Minimum > 0 {
|
||||
fmt.Fprintf(w, "Minimum:\t%d\n", r.Minimum)
|
||||
}
|
||||
if r.Default != "" {
|
||||
fmt.Fprintf(w, "Default:\t%s\n", r.Default)
|
||||
}
|
||||
if r.Value != "" {
|
||||
fmt.Fprintf(w, "Current Value:\t%s\n", r.Value)
|
||||
}
|
||||
if r.Source != "" {
|
||||
fmt.Fprintf(w, "Source:\t%s\n", r.Source)
|
||||
}
|
||||
w.Flush()
|
||||
}
|
||||
Reference in New Issue
Block a user