mirror of
https://github.com/Gouryella/drip.git
synced 2026-02-23 21:00:44 +00:00
Added predefined tunnel functionality, allowing users to define multiple tunnels in the configuration file and start them by name, including the following improvements: - Added --all flag to start all configured tunnels - Added parameterless start command to list available tunnels - Support configuration of multiple tunnel types (http, https, tcp) - Support advanced configurations such as subdomains, transport protocols, and IP allowlists refactor(deployments): Refactor Docker deployment configuration Removed old Dockerfile and Compose configurations, added new deployment files: - Removed .env.example and old Docker build files - Added Caddy reverse proxy configuration file - Added two deployment modes: standard and Caddy reverse proxy - Added detailed server configuration example files docs: Update documentation to include tunnel configuration and deployment guide Updated Chinese and English README documents: - Added usage instructions and configuration examples for predefined tunnels - Expanded server deployment section to include direct TLS and reverse proxy modes - Added server configuration reference table with detailed configuration item descriptions - Added specific configuration methods for Caddy and Nginx reverse proxies
296 lines
6.8 KiB
Go
296 lines
6.8 KiB
Go
package cli
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"strings"
|
|
|
|
"drip/internal/shared/ui"
|
|
"drip/pkg/config"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
var configCmd = &cobra.Command{
|
|
Use: "config",
|
|
Short: "Manage configuration",
|
|
Long: "Manage Drip client configuration (server, token, tunnels)",
|
|
}
|
|
|
|
var configInitCmd = &cobra.Command{
|
|
Use: "init",
|
|
Short: "Initialize configuration interactively",
|
|
Long: "Initialize Drip configuration with interactive prompts",
|
|
RunE: runConfigInit,
|
|
}
|
|
|
|
var configShowCmd = &cobra.Command{
|
|
Use: "show",
|
|
Short: "Show current configuration",
|
|
Long: "Display the current Drip configuration",
|
|
RunE: runConfigShow,
|
|
}
|
|
|
|
var configSetCmd = &cobra.Command{
|
|
Use: "set",
|
|
Short: "Set configuration values",
|
|
Long: "Set specific configuration values (server, token)",
|
|
RunE: runConfigSet,
|
|
}
|
|
|
|
var configResetCmd = &cobra.Command{
|
|
Use: "reset",
|
|
Short: "Reset configuration",
|
|
Long: "Delete the configuration file",
|
|
RunE: runConfigReset,
|
|
}
|
|
|
|
var configValidateCmd = &cobra.Command{
|
|
Use: "validate",
|
|
Short: "Validate configuration",
|
|
Long: "Validate the configuration file",
|
|
RunE: runConfigValidate,
|
|
}
|
|
|
|
var (
|
|
configFull bool
|
|
configForce bool
|
|
configServer string
|
|
configToken string
|
|
)
|
|
|
|
func init() {
|
|
configCmd.AddCommand(configInitCmd)
|
|
configCmd.AddCommand(configShowCmd)
|
|
configCmd.AddCommand(configSetCmd)
|
|
configCmd.AddCommand(configResetCmd)
|
|
configCmd.AddCommand(configValidateCmd)
|
|
|
|
configShowCmd.Flags().BoolVar(&configFull, "full", false, "Show full token (not hidden)")
|
|
|
|
configSetCmd.Flags().StringVar(&configServer, "server", "", "Server address (e.g., tunnel.example.com:443)")
|
|
configSetCmd.Flags().StringVar(&configToken, "token", "", "Authentication token")
|
|
|
|
configResetCmd.Flags().BoolVar(&configForce, "force", false, "Force reset without confirmation")
|
|
|
|
rootCmd.AddCommand(configCmd)
|
|
}
|
|
|
|
func runConfigInit(_ *cobra.Command, _ []string) error {
|
|
fmt.Print(ui.RenderConfigInit())
|
|
|
|
reader := bufio.NewReader(os.Stdin)
|
|
|
|
fmt.Print(ui.Muted("Server address (e.g., tunnel.example.com:443): "))
|
|
serverAddr, _ := reader.ReadString('\n')
|
|
serverAddr = strings.TrimSpace(serverAddr)
|
|
|
|
if serverAddr == "" {
|
|
return fmt.Errorf("server address is required")
|
|
}
|
|
|
|
fmt.Print(ui.Muted("Authentication token (leave empty to skip): "))
|
|
token, _ := reader.ReadString('\n')
|
|
token = strings.TrimSpace(token)
|
|
|
|
cfg := &config.ClientConfig{
|
|
Server: serverAddr,
|
|
Token: token,
|
|
TLS: true,
|
|
}
|
|
|
|
if err := config.SaveClientConfig(cfg, ""); err != nil {
|
|
return fmt.Errorf("failed to save configuration: %w", err)
|
|
}
|
|
|
|
fmt.Println(ui.RenderConfigSaved(config.DefaultClientConfigPath()))
|
|
|
|
return nil
|
|
}
|
|
|
|
func runConfigShow(_ *cobra.Command, _ []string) error {
|
|
cfg, err := config.LoadClientConfig("")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var displayToken string
|
|
if cfg.Token != "" {
|
|
if configFull {
|
|
displayToken = cfg.Token
|
|
} else {
|
|
tokenLen := len(cfg.Token)
|
|
if tokenLen <= 3 {
|
|
displayToken = "***"
|
|
} else if tokenLen > 10 {
|
|
displayToken = cfg.Token[:3] + "***" + cfg.Token[tokenLen-3:]
|
|
} else {
|
|
displayToken = cfg.Token[:3] + "***"
|
|
}
|
|
}
|
|
} else {
|
|
displayToken = ""
|
|
}
|
|
|
|
fmt.Println(ui.RenderConfigShow(cfg.Server, displayToken, !configFull, cfg.TLS, config.DefaultClientConfigPath()))
|
|
|
|
// Show tunnels if configured
|
|
if len(cfg.Tunnels) > 0 {
|
|
fmt.Println()
|
|
fmt.Println(ui.Title("Configured Tunnels"))
|
|
for _, t := range cfg.Tunnels {
|
|
addr := t.Address
|
|
if addr == "" {
|
|
addr = "127.0.0.1"
|
|
}
|
|
fmt.Printf(" %-12s %-6s %s:%d", t.Name, t.Type, addr, t.Port)
|
|
if t.Subdomain != "" {
|
|
fmt.Printf(" subdomain=%s", t.Subdomain)
|
|
}
|
|
if t.Transport != "" {
|
|
fmt.Printf(" transport=%s", t.Transport)
|
|
}
|
|
if len(t.AllowIPs) > 0 {
|
|
fmt.Printf(" allow=%s", strings.Join(t.AllowIPs, ","))
|
|
}
|
|
if len(t.DenyIPs) > 0 {
|
|
fmt.Printf(" deny=%s", strings.Join(t.DenyIPs, ","))
|
|
}
|
|
fmt.Println()
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func runConfigSet(_ *cobra.Command, _ []string) error {
|
|
cfg, err := config.LoadClientConfig("")
|
|
if err != nil {
|
|
cfg = &config.ClientConfig{
|
|
TLS: true,
|
|
}
|
|
}
|
|
|
|
modified := false
|
|
var updates []string
|
|
|
|
if configServer != "" {
|
|
cfg.Server = configServer
|
|
modified = true
|
|
updates = append(updates, "Server updated: "+configServer)
|
|
}
|
|
|
|
if configToken != "" {
|
|
cfg.Token = configToken
|
|
modified = true
|
|
updates = append(updates, "Token updated")
|
|
}
|
|
|
|
if !modified {
|
|
return fmt.Errorf("no changes specified. Use --server or --token")
|
|
}
|
|
|
|
if err := config.SaveClientConfig(cfg, ""); err != nil {
|
|
return fmt.Errorf("failed to save configuration: %w", err)
|
|
}
|
|
|
|
fmt.Println(ui.RenderConfigUpdated(updates))
|
|
|
|
return nil
|
|
}
|
|
|
|
func runConfigReset(_ *cobra.Command, _ []string) error {
|
|
configPath := config.DefaultClientConfigPath()
|
|
|
|
if !config.ConfigExists("") {
|
|
fmt.Println("No configuration file found")
|
|
return nil
|
|
}
|
|
|
|
if !configForce {
|
|
fmt.Print("Are you sure you want to delete the configuration? (y/N): ")
|
|
reader := bufio.NewReader(os.Stdin)
|
|
response, _ := reader.ReadString('\n')
|
|
response = strings.ToLower(strings.TrimSpace(response))
|
|
|
|
if response != "y" && response != "yes" {
|
|
fmt.Println("Cancelled")
|
|
return nil
|
|
}
|
|
}
|
|
|
|
if err := os.Remove(configPath); err != nil {
|
|
return fmt.Errorf("failed to delete configuration: %w", err)
|
|
}
|
|
|
|
fmt.Println(ui.RenderConfigDeleted())
|
|
|
|
return nil
|
|
}
|
|
|
|
func runConfigValidate(_ *cobra.Command, _ []string) error {
|
|
cfg, err := config.LoadClientConfig("")
|
|
if err != nil {
|
|
fmt.Println(ui.Error("Failed to load configuration"))
|
|
return err
|
|
}
|
|
|
|
serverValid, serverMsg := validateServerAddress(cfg.Server)
|
|
tokenSet := cfg.Token != ""
|
|
tlsEnabled := cfg.TLS
|
|
|
|
tokenMsg := "Token is set"
|
|
if !tokenSet {
|
|
tokenMsg = "Token is not set (authentication may fail)"
|
|
}
|
|
|
|
fmt.Println(ui.RenderConfigValidation(serverValid, serverMsg, tokenSet, tokenMsg, tlsEnabled))
|
|
|
|
// Validate tunnels
|
|
if len(cfg.Tunnels) > 0 {
|
|
fmt.Println()
|
|
fmt.Println(ui.Title("Tunnel Validation"))
|
|
allValid := true
|
|
for _, t := range cfg.Tunnels {
|
|
if err := t.Validate(); err != nil {
|
|
fmt.Printf(" ✗ %s: %v\n", t.Name, err)
|
|
allValid = false
|
|
} else {
|
|
fmt.Printf(" ✓ %s: valid\n", t.Name)
|
|
}
|
|
}
|
|
if !allValid {
|
|
return fmt.Errorf("some tunnels have invalid configuration")
|
|
}
|
|
}
|
|
|
|
if !serverValid {
|
|
return fmt.Errorf("invalid configuration: %s", serverMsg)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func validateServerAddress(addr string) (bool, string) {
|
|
addr = strings.TrimSpace(addr)
|
|
if addr == "" {
|
|
return false, "Server address is not set"
|
|
}
|
|
|
|
host, port, err := net.SplitHostPort(addr)
|
|
if err != nil {
|
|
return false, fmt.Sprintf("Server address must include host and port (e.g., tunnel.example.com:443): %v", err)
|
|
}
|
|
|
|
if host == "" {
|
|
return false, "Server host is empty"
|
|
}
|
|
|
|
if port == "" {
|
|
return false, "Server port is empty"
|
|
}
|
|
|
|
return true, fmt.Sprintf("Server address is valid (%s:%s)", host, port)
|
|
}
|