feat(cli): add the prx command-line interface
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"prx/internal/config"
|
||||
"prx/internal/proto"
|
||||
"prx/internal/state"
|
||||
)
|
||||
|
||||
func (a *app) newBypassCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "bypass",
|
||||
Short: "Manage destinations that must bypass the proxy",
|
||||
}
|
||||
cmd.AddCommand(a.newBypassAddCmd(), a.newBypassRmCmd(), a.newBypassLsCmd())
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (a *app) newBypassAddCmd() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "add <cidr|ip|domain>",
|
||||
Short: "Add a CIDR/IP (routed direct) or domain (engine hint) to the bypass list",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cfg, err := a.loadConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
arg := args[0]
|
||||
var changed bool
|
||||
if config.IsCIDR(arg) {
|
||||
changed, err = cfg.AddExtraCIDR(arg)
|
||||
} else {
|
||||
changed, err = cfg.AddDomain(arg)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !changed {
|
||||
fmt.Fprintln(a.out, "already present")
|
||||
return nil
|
||||
}
|
||||
if err := config.Save(a.configPath, cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(a.out, "added %s to bypass list\n", arg)
|
||||
return a.reapplyIfEnabled(cmd.Context(), cfg)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (a *app) newBypassRmCmd() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "rm <cidr|ip|domain>",
|
||||
Short: "Remove an entry from the bypass list",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cfg, err := a.loadConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
arg := args[0]
|
||||
var changed bool
|
||||
if config.IsCIDR(arg) {
|
||||
changed, err = cfg.RemoveExtraCIDR(arg)
|
||||
} else {
|
||||
changed = cfg.RemoveDomain(arg)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !changed {
|
||||
fmt.Fprintln(a.out, "not present")
|
||||
return nil
|
||||
}
|
||||
if err := config.Save(a.configPath, cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(a.out, "removed %s from bypass list\n", arg)
|
||||
return a.reapplyIfEnabled(cmd.Context(), cfg)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (a *app) newBypassLsCmd() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "ls",
|
||||
Short: "List bypass entries",
|
||||
Args: cobra.NoArgs,
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
cfg, err := a.loadConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
writeList(a.out, "always (engine hints):", cfg.Bypass.AlwaysCIDRs, false)
|
||||
writeList(a.out, "extra CIDRs (routed direct):", cfg.Bypass.ExtraCIDRs, true)
|
||||
writeList(a.out, "domains (engine hints):", cfg.Bypass.Domains, true)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// writeList prints a labelled list, showing "(none)" when empty and showEmpty.
|
||||
func writeList(w io.Writer, label string, items []string, showEmpty bool) {
|
||||
fmt.Fprintln(w, label)
|
||||
if len(items) == 0 && showEmpty {
|
||||
fmt.Fprintln(w, " (none)")
|
||||
}
|
||||
for _, item := range items {
|
||||
fmt.Fprintln(w, " "+item)
|
||||
}
|
||||
}
|
||||
|
||||
// reapplyIfEnabled refreshes the routing rules when prx is currently enabled so
|
||||
// bypass changes take effect immediately.
|
||||
func (a *app) reapplyIfEnabled(ctx context.Context, cfg *config.Config) error {
|
||||
st, err := state.Load(cfg.State.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !st.Enabled {
|
||||
fmt.Fprintln(a.out, "takes effect on the next 'prx on'")
|
||||
return nil
|
||||
}
|
||||
if err := a.dispatch(ctx, cfg, proto.ActionApply, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintln(a.out, "routing rules refreshed")
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user