From a2a1fb2f63b34af5383e026c134f4a5a23de43af Mon Sep 17 00:00:00 2001 From: zhilv Date: Thu, 4 Dec 2025 00:14:42 +0800 Subject: [PATCH] =?UTF-8?q?feat(config):=20=E6=B7=BB=E5=8A=A0=20JSON/TOML?= =?UTF-8?q?=20=E9=85=8D=E7=BD=AE=E8=AF=BB=E5=8F=96=E4=B8=8E=20YAML/JSON/TO?= =?UTF-8?q?ML=20=E5=AF=BC=E5=87=BA=E5=8A=9F=E8=83=BD,=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E9=83=A8=E5=88=86=E5=86=85=E9=83=A8=E4=BB=A3=E7=A0=81=E7=BB=93?= =?UTF-8?q?=E6=9E=84,=20=E6=9B=B4=E6=96=B0=20Makefile=EF=BC=9A=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=20Zig=20=E5=A4=96=E9=93=BE=E6=A3=80=E6=B5=8B=E3=80=81?= =?UTF-8?q?=E5=8F=AF=E9=80=89=20UPX=20=E5=8E=8B=E7=BC=A9=E3=80=81=E6=9E=84?= =?UTF-8?q?=E5=BB=BA=E6=B5=81=E7=A8=8B=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 65 ++++++++++++++++++++++----- cmd/dhcp.go | 19 +++----- cmd/export.go | 89 +++++++++++++++++++++++++++++++++++++ cmd/static.go | 16 +++---- go.mod | 1 + go.sum | 1 + internal/model/classroom.go | 14 +++--- internal/model/named.go | 5 --- internal/net/nic.go | 18 +++----- 9 files changed, 166 insertions(+), 62 deletions(-) create mode 100644 cmd/export.go delete mode 100644 internal/model/named.go diff --git a/Makefile b/Makefile index 297b727..21c400a 100644 --- a/Makefile +++ b/Makefile @@ -1,23 +1,64 @@ -APP_NAME = netctl -SRC = . -OUTPUT = ./bin/$(APP_NAME).exe -GO = go +APP_NAME := netctl +SRC := . +OUTPUT := ./bin/$(APP_NAME).exe +GO := go -# 获取 Git commit +# Version info +VERSION := 1.0.1 GIT_COMMIT := $(shell git rev-parse --short HEAD 2>/dev/null || echo none) -# 获取当前时间 UTC BUILD_TIME := $(shell date -u +%Y-%m-%dT%H:%M:%SZ) -.PHONY: build run clean +# Detect tools +DETECTED_ZIG := $(shell command -v zig 2>/dev/null) +DETECTED_UPX := $(shell command -v upx 2>/dev/null) + +# User config +ZIG ?= 1 # auto enable zig linker if exists +BRUTE ?= 0 # UPX brute mode disabled by default + +.PHONY: all build run clean + +all: build -# 编译命令 build: - $(GO) build -ldflags "-X 'netcli/cmd.Version=1.0.0' -X 'netcli/cmd.Commit=$(GIT_COMMIT)' -X 'netcli/cmd.BuildTime=$(BUILD_TIME)'" -o $(OUTPUT) $(SRC) + @mkdir -p ./bin + + @if [ "$(ZIG)" = "1" ] && [ -n "$(DETECTED_ZIG)" ]; then \ + echo "Zig detected -> Using Zig as Go external linker"; \ + export CGO_ENABLED=0; \ + export CC="zig cc"; \ + export CXX="zig c++"; \ + $(GO) build -trimpath -ldflags "-s -w \ + -X 'netcli/cmd.Version=$(VERSION)' \ + -X 'netcli/cmd.Commit=$(GIT_COMMIT)' \ + -X 'netcli/cmd.BuildTime=$(BUILD_TIME)'" \ + -o $(OUTPUT) $(SRC); \ + else \ + echo "Zig not used → Using Go native linker"; \ + $(GO) build -trimpath -ldflags "-s -w \ + -X 'netcli/cmd.Version=$(VERSION)' \ + -X 'netcli/cmd.Commit=$(GIT_COMMIT)' \ + -X 'netcli/cmd.BuildTime=$(BUILD_TIME)'" \ + -o $(OUTPUT) $(SRC); \ + fi + + @if [ -n "$(DETECTED_UPX)" ]; then \ + if [ "$(BRUTE)" = "1" ]; then \ + echo "UPX brute mode enabled (--ultra-brute)..."; \ + upx --ultra-brute $(OUTPUT); \ + else \ + echo "UPX detected → Applying normal compression (--best --lzma)..."; \ + upx --best --lzma $(OUTPUT); \ + fi \ + else \ + echo "UPX not found → Skipping compression"; \ + fi -# 直接运行 run: - $(GO) run -ldflags "-X 'netcli/cmd.Version=1.0.0' -X 'netcli/cmd.Commit=$(GIT_COMMIT)' -X 'netcli/cmd.BuildTime=$(BUILD_TIME)'" $(SRC) + $(GO) run -ldflags "-s -w \ + -X 'netcli/cmd.Version=$(VERSION)' \ + -X 'netcli/cmd.Commit=$(GIT_COMMIT)' \ + -X 'netcli/cmd.BuildTime=$(BUILD_TIME)'" $(SRC) -# 清理编译文件 clean: rm -f $(OUTPUT) diff --git a/cmd/dhcp.go b/cmd/dhcp.go index ec89e15..ff2aeb7 100644 --- a/cmd/dhcp.go +++ b/cmd/dhcp.go @@ -2,10 +2,10 @@ package cmd import ( "fmt" - stdnet "net" + "net" "netcli/internal/constants" - "netcli/internal/net" + netmod "netcli/internal/net" "github.com/spf13/cobra" ) @@ -16,30 +16,25 @@ var dhcpCmd = &cobra.Command{ Short: "将指定网卡改为 DHCP 自动获取 IP", RunE: func(cmd *cobra.Command, args []string) error { // 检查管理员权限 - if !net.IsAdmin() { + if !netmod.IsAdmin() { fmt.Println("程序需要管理员权限,尝试提升...") - return net.RunAsAdmin() + return netmod.RunAsAdmin() } // 获取所有网卡 - ifaces, err := stdnet.Interfaces() + ifaces, err := net.Interfaces() if err != nil { return fmt.Errorf("获取网卡失败: %w", err) } - var nics []net.NetIfWrap - for _, i := range ifaces { - nics = append(nics, net.NetIfWrap{i}) - } - // 用户选择网卡 - nic, err := net.ChooseNic(nics, constants.DefaultDHCPMsg) + nic, err := netmod.ChooseNic(ifaces, constants.DefaultDHCPMsg) if err != nil { return err } // 执行 netsh 命令设置 DHCP - if err := net.SetDHCP(nic); err != nil { + if err := netmod.SetDHCP(nic); err != nil { return err } diff --git a/cmd/export.go b/cmd/export.go new file mode 100644 index 0000000..7ed9595 --- /dev/null +++ b/cmd/export.go @@ -0,0 +1,89 @@ +package cmd + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + "strings" + + "netcli/config" + + "github.com/pelletier/go-toml/v2" + "github.com/spf13/cobra" + "gopkg.in/yaml.v3" +) + +var exportCmd = &cobra.Command{ + Use: "export", + Short: "导出默认配置文件", + Long: "可以将默认配置导出为 YAML, TOML, JSON 格式文件,支持自定义路径和格式, 生效顺序: YAML, TOML, JSON", + RunE: func(cmd *cobra.Command, args []string) error { + // 获取参数 + filePath, _ := cmd.Flags().GetString("file") + format, _ := cmd.Flags().GetString("format") + + // 默认文件名和格式 + if filePath == "" { + filePath = "config.yaml" + } + if format == "" { + format = "yaml" + } + + format = strings.ToLower(format) + if format != "yaml" && format != "yml" && format != "toml" && format != "json" { + return fmt.Errorf("不支持的格式: %s, 可选: yaml, toml, json", format) + } + + // 根据格式修改文件后缀 + ext := strings.ToLower(filepath.Ext(filePath)) + if ext == "" || ext != "."+format { + filePath = strings.TrimSuffix(filePath, ext) + "." + format + } + + // 获取默认配置 + defaultConfig := config.DefaultConfig() + + // 创建文件 + f, err := os.Create(filePath) + if err != nil { + return fmt.Errorf("创建文件失败: %w", err) + } + defer f.Close() + + // 写入不同格式 + switch format { + case "yaml", "yml": + enc := yaml.NewEncoder(f) + defer enc.Close() + if err := enc.Encode(defaultConfig); err != nil { + return fmt.Errorf("YAML 编码失败: %w", err) + } + case "json": + enc := json.NewEncoder(f) + enc.SetIndent("", " ") + if err := enc.Encode(defaultConfig); err != nil { + return fmt.Errorf("JSON 编码失败: %w", err) + } + case "toml": + data, err := toml.Marshal(defaultConfig) + if err != nil { + return fmt.Errorf("TOML 编码失败: %w", err) + } + if _, err := f.Write(data); err != nil { + return fmt.Errorf("写入 TOML 文件失败: %w", err) + } + } + + absPath, _ := filepath.Abs(filePath) + fmt.Printf("默认配置已导出到: %s\n", absPath) + return nil + }, +} + +func init() { + exportCmd.Flags().StringP("file", "f", "", "导出文件路径 (默认 ./config.yaml)") + exportCmd.Flags().StringP("format", "t", "yaml", "导出格式: yaml, toml, json (默认 yaml)") + rootCmd.AddCommand(exportCmd) +} diff --git a/cmd/static.go b/cmd/static.go index adf6fb3..ab676af 100644 --- a/cmd/static.go +++ b/cmd/static.go @@ -25,20 +25,14 @@ var staticCmd = &cobra.Command{ // 获取本机网卡 ifaces, _ := stdnet.Interfaces() - var nics []netmod.NetIfWrap - for _, i := range ifaces { - nics = append(nics, netmod.NetIfWrap{i}) - } - nic, _ := netmod.ChooseNic(nics, constants.DefaultStaticMsg) + nic, _ := netmod.ChooseNic(ifaces, constants.DefaultStaticMsg) // 加载配置文件 - cfg, _ := config.LoadConfig(constants.DefaultConfig) - var rooms []model.ClassRoom - if cfg != nil && len(cfg.ClassRooms) > 0 { - rooms = cfg.ClassRooms - } else { - rooms = config.DefaultClassRooms() + cfg := config.LoadDefaultConfig() + rooms := cfg.ClassRooms + if len(rooms) == 0 { + rooms = config.DefaultConfig().ClassRooms } // 用户选择教室 diff --git a/go.mod b/go.mod index d7c6b3f..f9a281b 100644 --- a/go.mod +++ b/go.mod @@ -24,4 +24,5 @@ require ( golang.org/x/sys v0.29.0 // indirect golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect golang.org/x/text v0.28.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 6b17ef6..70c70e9 100644 --- a/go.sum +++ b/go.sum @@ -80,4 +80,5 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/model/classroom.go b/internal/model/classroom.go index 17df4f2..63f7bd1 100644 --- a/internal/model/classroom.go +++ b/internal/model/classroom.go @@ -1,13 +1,9 @@ package model type ClassRoom struct { - ID string - Name string - Addr string - Mask string - Gateway string -} - -func (c ClassRoom) GetName() string { - return c.Name + ID string `mapstructure:"id"` + Name string `mapstructure:"name"` + Addr string `mapstructure:"addr"` + Mask string `mapstructure:"mask"` + Gateway string `mapstructure:"gateway"` } diff --git a/internal/model/named.go b/internal/model/named.go deleted file mode 100644 index 81026fa..0000000 --- a/internal/model/named.go +++ /dev/null @@ -1,5 +0,0 @@ -package model - -type Named interface { - GetName() string -} diff --git a/internal/net/nic.go b/internal/net/nic.go index 947e294..2884a25 100644 --- a/internal/net/nic.go +++ b/internal/net/nic.go @@ -10,16 +10,8 @@ import ( "golang.org/x/sys/windows" ) -type NetIfWrap struct { - net.Interface -} - -func (n NetIfWrap) GetName() string { - return n.Interface.Name -} - // 选择网卡 -func ChooseNic(nics []NetIfWrap, message string) (NetIfWrap, error) { +func ChooseNic(nics []net.Interface, message string) (net.Interface, error) { var names []string for _, nic := range nics { names = append(names, nic.Name) @@ -30,18 +22,18 @@ func ChooseNic(nics []NetIfWrap, message string) (NetIfWrap, error) { Options: names, }, &choice) if err != nil { - return NetIfWrap{}, err + return net.Interface{}, err } for _, nic := range nics { if nic.Name == choice { return nic, nil } } - return NetIfWrap{}, fmt.Errorf("未找到网卡: %s", choice) + return net.Interface{}, fmt.Errorf("未找到网卡: %s", choice) } // 设置静态IP -func SetStaticIP(nic NetIfWrap, addr, mask, gateway string) error { +func SetStaticIP(nic net.Interface, addr, mask, gateway string) error { cmd := exec.Command("netsh", "interface", "ip", "set", "address", fmt.Sprintf("name=%s", nic.Name), "static", addr, mask, gateway) @@ -53,7 +45,7 @@ func SetStaticIP(nic NetIfWrap, addr, mask, gateway string) error { } // 设置 DHCP -func SetDHCP(nic NetIfWrap) error { +func SetDHCP(nic net.Interface) error { cmd := exec.Command("netsh", "interface", "ip", "set", "address", fmt.Sprintf("name=%s", nic.Name), "dhcp") out, err := cmd.CombinedOutput()