diff --git a/go.mod b/go.mod index 2531fde..7a52621 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,7 @@ require ( require ( filippo.io/edwards25519 v1.1.0 // indirect + github.com/BurntSushi/toml v1.6.0 // indirect github.com/atotto/clipboard v0.1.4 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/charmbracelet/colorprofile v0.4.1 // indirect diff --git a/go.sum b/go.sum index 61d1ff2..2ab29a3 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk= +github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= diff --git a/pkg/wizard/config.go b/pkg/wizard/config.go index 92dae59..8f49ce1 100644 --- a/pkg/wizard/config.go +++ b/pkg/wizard/config.go @@ -1,164 +1,228 @@ package wizard import ( - "bufio" - "database/sql" - "fmt" + "errors" "net" "os" - "strings" "sunhpc/pkg/database" + "sunhpc/pkg/logger" "sunhpc/pkg/utils" + + "github.com/BurntSushi/toml" ) // 配置项映射:定义每个配置项对应的表名、键名 -var configMappings = []struct { - table string - key string - getVal func(m *model) interface{} // 动态获取配置值的函数 -}{ - // attributes 表 - {"attributes", "license", func(m *model) any { return m.config.License }}, - {"attributes", "accepted", func(m *model) any { return m.config.AgreementAccepted }}, - {"attributes", "country", func(m *model) any { return m.config.Country }}, - {"attributes", "region", func(m *model) any { return m.config.Region }}, - {"attributes", "timezone", func(m *model) any { return m.config.Timezone }}, - {"attributes", "homepage", func(m *model) any { return m.config.HomePage }}, - {"attributes", "dbaddress", func(m *model) any { return m.config.DBAddress }}, - {"attributes", "software", func(m *model) any { return m.config.Software }}, +type ConfigMapping struct { + Title string `toml:"title"` + Base struct { + ClusterName string `toml:"cluster_name"` + Country string `toml:"country"` + State string `toml:"state"` + City string `toml:"city"` + HomePage string `toml:"homepage"` + Contact string `toml:"contact"` + License string `toml:"license"` + BaseDir string `toml:"base_dir"` + WorkDir string `toml:"work_dir"` + DistroDir string `toml:"distro_dir"` + Partition string `toml:"partition"` + Distribution string `toml:"distribution"` + Timezone string `toml:"timezone"` + SafePort string `toml:"safe_port"` + SafeDirs string `toml:"safe_dirs"` + SafeSecurity string `toml:"safe_security"` + PluginDirs string `toml:"plugin_dirs"` + PluginPort string `toml:"plugin_port"` + GangliaAddr string `toml:"ganglia_addr"` + } `toml:"base"` + Pxelinux struct { + NextServer string `toml:"next_server"` + PxeFilename string `toml:"pxe_filename"` + PxeLinuxDir string `toml:"pxelinux_dir"` + BootArgs string `toml:"boot_args"` + } `toml:"pxelinux"` + Public struct { + PublicHostname string `toml:"public_hostname"` + PublicInterface string `toml:"public_interface"` + PublicAddress string `toml:"public_address"` + PublicNetmask string `toml:"public_netmask"` + PublicGateway string `toml:"public_gateway"` + PublicNetwork string `toml:"public_network"` + PublicDomain string `toml:"public_domain"` + PublicCIDR string `toml:"public_cidr"` + PublicDNS string `toml:"public_dns"` + PublicMac string `toml:"public_mac"` + PublicMTU string `toml:"public_mtu"` + PublicNTP string `toml:"public_ntp"` + } `toml:"public"` + Private struct { + PrivateHostname string `toml:"private_hostname"` + PrivateInterface string `toml:"private_interface"` + PrivateAddress string `toml:"private_address"` + PrivateNetmask string `toml:"private_netmask"` + PrivateNetwork string `toml:"private_network"` + PrivateDomain string `toml:"private_domain"` + PrivateCIDR string `toml:"private_cidr"` + PrivateMac string `toml:"private_mac"` + PrivateMTU string `toml:"private_mtu"` + } `toml:"private"` +} - // nodes 表 - {"nodes", "name", func(m *model) any { return m.config.Hostname }}, +func NewConfigWithDefault() *ConfigMapping { + return &ConfigMapping{ + Title: "Cluster Configuration", + Base: struct { + ClusterName string `toml:"cluster_name"` + Country string `toml:"country"` + State string `toml:"state"` + City string `toml:"city"` + HomePage string `toml:"homepage"` + Contact string `toml:"contact"` + License string `toml:"license"` + BaseDir string `toml:"base_dir"` + WorkDir string `toml:"work_dir"` + DistroDir string `toml:"distro_dir"` + Partition string `toml:"partition"` + Distribution string `toml:"distribution"` + Timezone string `toml:"timezone"` + SafePort string `toml:"safe_port"` + SafeDirs string `toml:"safe_dirs"` + SafeSecurity string `toml:"safe_security"` + PluginDirs string `toml:"plugin_dirs"` + PluginPort string `toml:"plugin_port"` + GangliaAddr string `toml:"ganglia_addr"` + }{ + ClusterName: "SunHPC_Cluster", + Country: "CN", + State: "Beijing", + City: "Beijing", + HomePage: "https://www.sunhpc.com", + Contact: "admin@sunhpc.com", + License: "MIT", + BaseDir: "install", + WorkDir: "/export", + DistroDir: "/export/sunhpc", + Partition: "default", + Distribution: "sunhpc-dist", + Timezone: "Asia/Shanghai", + SafePort: "372", + SafeDirs: "safe.d", + SafeSecurity: "safe-security", + PluginDirs: "/etc/sunhpc/plugin", + PluginPort: "12123", + GangliaAddr: "224.0.0.3", + }, + Pxelinux: struct { + NextServer string `toml:"next_server"` + PxeFilename string `toml:"pxe_filename"` + PxeLinuxDir string `toml:"pxelinux_dir"` + BootArgs string `toml:"boot_args"` + }{ + NextServer: "192.168.1.1", + PxeFilename: "pxelinux.0", + PxeLinuxDir: "/tftpboot/pxelinux", + BootArgs: "net.ifnames=0 biosdevname=0", + }, + Public: struct { + PublicHostname string `toml:"public_hostname"` + PublicInterface string `toml:"public_interface"` + PublicAddress string `toml:"public_address"` + PublicNetmask string `toml:"public_netmask"` + PublicGateway string `toml:"public_gateway"` + PublicNetwork string `toml:"public_network"` + PublicDomain string `toml:"public_domain"` + PublicCIDR string `toml:"public_cidr"` + PublicDNS string `toml:"public_dns"` + PublicMac string `toml:"public_mac"` + PublicMTU string `toml:"public_mtu"` + PublicNTP string `toml:"public_ntp"` + }{ + PublicHostname: "cluster.hpc.org", + PublicInterface: "eth0", + PublicAddress: "", + PublicNetmask: "", + PublicGateway: "", + PublicNetwork: "", + PublicDomain: "hpc.org", + PublicCIDR: "", + PublicDNS: "", + PublicMac: "00:11:22:33:44:55", + PublicMTU: "1500", + PublicNTP: "pool.ntp.org", + }, + Private: struct { + PrivateHostname string `toml:"private_hostname"` + PrivateInterface string `toml:"private_interface"` + PrivateAddress string `toml:"private_address"` + PrivateNetmask string `toml:"private_netmask"` + PrivateNetwork string `toml:"private_network"` + PrivateDomain string `toml:"private_domain"` + PrivateCIDR string `toml:"private_cidr"` + PrivateMac string `toml:"private_mac"` + PrivateMTU string `toml:"private_mtu"` + }{ + PrivateHostname: "sunhpc", + PrivateInterface: "eth1", + PrivateAddress: "172.16.9.254", + PrivateNetmask: "255.255.255.0", + PrivateNetwork: "172.16.9.0", + PrivateDomain: "example.com", + PrivateCIDR: "172.16.9.0/24", + PrivateMac: "00:11:22:33:44:66", + PrivateMTU: "1500", + }, + } +} - // 公网设置表 - {"public_network", "public_interface", func(m *model) any { return m.config.PublicInterface }}, - {"public_network", "ip_address", func(m *model) any { return m.config.PublicIPAddress }}, - {"public_network", "netmask", func(m *model) any { return m.config.PublicNetmask }}, - {"public_network", "gateway", func(m *model) any { return m.config.PublicGateway }}, - // 内网配置表 - {"internal_network", "internal_interface", func(m *model) any { return m.config.InternalInterface }}, - {"internal_network", "internal_ip", func(m *model) any { return m.config.InternalIPAddress }}, - {"internal_network", "internal_mask", func(m *model) any { return m.config.InternalNetmask }}, - // DNS配置表 - {"dns_config", "dns_primary", func(m *model) any { return m.config.DNSPrimary }}, - {"dns_config", "dns_secondary", func(m *model) any { return m.config.DNSSecondary }}, +func loadConfig() (*ConfigMapping, error) { + configs := NewConfigWithDefault() + cfgfile := "/etc/sunhpc/config.toml" + + // 尝试解析配置文件 + if _, err := toml.DecodeFile(cfgfile, configs); err != nil { + if errors.Is(err, os.ErrNotExist) { + // 文件不存在,直接返回默认配置 + logger.Infof("Config file %s not exist, use default config", cfgfile) + return configs, nil + } + // 其他错误,返回错误 + logger.Debugf("[DEBUG] Parse config file %s failed: %v", cfgfile, err) + return nil, err + } + + logger.Infof("Load config file %s success", cfgfile) + return configs, nil } // saveConfig 入口函数:保存所有配置到数据库 func (m *model) saveConfig() error { + conn, err := database.GetDB() // 假设database包已实现getDB()获取连接 if err != nil { - return fmt.Errorf("获取数据库连接失败: %w", err) + logger.Errorf("Database connection failed: %v", err) + return err } defer conn.Close() m.force = false // 初始化全量覆盖标识 - // 遍历所有配置项,逐个处理 - for _, item := range configMappings { - val := item.getVal(m) - exists, err := m.checkExists(conn, item.table, item.key) - if err != nil { - return fmt.Errorf("检查%s.%s是否存在失败: %w", item.table, item.key, err) - } - - // 根据存在性和用户选择处理 - if !exists { - // 不存在则直接插入 - if err := m.upsertConfig(conn, item.table, item.key, val, false); err != nil { - return fmt.Errorf("插入%s.%s失败: %w", item.table, item.key, err) - } - continue - } - - // 已存在:判断是否全量覆盖 - if m.force { - if err := m.upsertConfig(conn, item.table, item.key, val, true); err != nil { - return fmt.Errorf("强制更新%s.%s失败: %w", item.table, item.key, err) - } - continue - } - - // 询问用户操作 - choice, err := m.askUserChoice(item.table, item.key) - if err != nil { - return fmt.Errorf("获取用户选择失败: %w", err) - } - - switch strings.ToLower(choice) { - case "y", "yes": - // 单条覆盖 - if err := m.upsertConfig(conn, item.table, item.key, val, true); err != nil { - return fmt.Errorf("更新%s.%s失败: %w", item.table, item.key, err) - } - case "a", "all": - // 全量覆盖,后续不再询问 - m.force = true - if err := m.upsertConfig(conn, item.table, item.key, val, true); err != nil { - return fmt.Errorf("全量更新%s.%s失败: %w", item.table, item.key, err) - } - case "n", "no": - // 跳过当前项 - fmt.Printf("跳过%s.%s的更新\n", item.table, item.key) - default: - fmt.Printf("无效选择%s,跳过%s.%s的更新\n", choice, item.table, item.key) - } + config, err := loadConfig() + if err != nil { + logger.Debugf("[DEBUG] Load config file failed: %v", err) + return err } + t_value := m.config.PrivateIPAddress + c_value := config.Private.PrivateAddress + + logger.Debugf("t_value: %s\n", t_value) + logger.Debugf("c_value: %s\n", c_value) + + logger.Debugf("config.Base.ClusterName: %s\n", config.Base.ClusterName) + return nil } -// checkExists 集中判断配置项是否存在(核心判断逻辑) -func (m *model) checkExists(conn *sql.DB, table, key string) (bool, error) { - var count int - // 通用存在性检查SQL(假设所有表都有key字段作为主键) - query := fmt.Sprintf("SELECT COUNT(1) FROM %s WHERE `key` = ?", table) - err := conn.QueryRow(query, key).Scan(&count) - if err != nil { - // 表不存在也视为"不存在"(可选:根据实际需求调整,比如先建表) - if strings.Contains(err.Error(), "table not found") { - return false, nil - } - return false, err - } - return count > 0, nil -} - -// upsertConfig 统一处理插入/更新逻辑 -func (m *model) upsertConfig(conn *sql.DB, table, key string, val interface{}, update bool) error { - var query string - if !update { - // 插入:假设表结构为(key, value) - query = fmt.Sprintf("INSERT INTO %s (`key`, `value`) VALUES (?, ?)", table) - } else { - // 更新 - query = fmt.Sprintf("UPDATE %s SET `value` = ? WHERE `key` = ?", table) - } - - // 处理参数顺序(更新和插入的参数顺序不同) - var args []interface{} - if !update { - args = []interface{}{key, val} - } else { - args = []interface{}{val, key} - } - - _, err := conn.Exec(query, args...) - return err -} - -// askUserChoice 询问用户操作选择 -func (m *model) askUserChoice(table, key string) (string, error) { - reader := bufio.NewReader(os.Stdin) - fmt.Printf("配置项%s.%s已存在,选择操作(y/yes=覆盖, n/no=跳过, a/all=全量覆盖后续所有): ", table, key) - input, err := reader.ReadString('\n') - if err != nil { - return "", err - } - // 去除空格和换行 - return strings.TrimSpace(input), nil -} - // 获取系统网络接口 func getNetworkInterfaces() []string { // 实现获取系统网络接口的逻辑 diff --git a/pkg/wizard/focused.go b/pkg/wizard/focused.go index fc2f3ee..f933806 100644 --- a/pkg/wizard/focused.go +++ b/pkg/wizard/focused.go @@ -31,8 +31,8 @@ func NewTextInput(placeholder string, defaultValue string) *TextInput { ti := textinput.New() ti.Placeholder = placeholder ti.SetValue(defaultValue) - ti.Focus() - return &TextInput{Model: ti, focused: true} + ti.Blur() + return &TextInput{Model: ti, focused: false} } func (t *TextInput) Focus() tea.Cmd { @@ -135,9 +135,14 @@ func NewFocusManager(loop bool) *FocusManager { } } -// Register 注册可聚焦组件(指定标识和切换顺序) +/* +Register 注册可聚焦组件(指定标识和切换顺序) +ID 组件的唯一标识,用于后续切换和获取焦点 +例如 "form1.ip_input"、"form1.next_btn" +*/ func (fm *FocusManager) Register(id string, comp Focusable) { - // 防御性检查:避免 components 为空导致 panic + + // 防御性检查:避免 components 未初始化为nil导致 panic if fm.components == nil { fm.components = make(map[string]Focusable) } @@ -147,6 +152,8 @@ func (fm *FocusManager) Register(id string, comp Focusable) { return } + // id : accept_btn, form1.reject_btn + // comp: 接受协议按钮, 拒绝协议按钮 fm.components[id] = comp fm.order = append(fm.order, id) @@ -211,6 +218,7 @@ func (fm *FocusManager) Prev() tea.Cmd { return nil } + //fm.components[fm.currentFocusID].Blur() fm.components[fm.currentFocusID].Blur() prevID := fm.order[prevIdx] fm.currentFocusID = prevID @@ -259,23 +267,22 @@ func (m *model) initPageFocus(page PageType) { m.focusManager = NewFocusManager(true) - // 获取当前页面的组件 pageComps, exists := m.pageComponents[page] if !exists { return } - // 按 [业务逻辑顺序] 注册组件 (决定Tab切换的顺序) var componentOrder []string + var defaultFocusID string - // 按页面类型定义不同的注册顺序 switch page { case PageAgreement: componentOrder = []string{"accept_btn", "reject_btn"} + defaultFocusID = "accept_btn" case PageData: componentOrder = []string{ "Homepage_input", - "Hostname_input", + "ClusterName_input", "Country_input", "Region_input", "Timezone_input", @@ -284,23 +291,28 @@ func (m *model) initPageFocus(page PageType) { "next_btn", "prev_btn", } + defaultFocusID = "next_btn" case PagePublicNetwork: componentOrder = []string{ "PublicInterface_input", "PublicIPAddress_input", "PublicNetmask_input", "PublicGateway_input", + "PublicMTU_input", "next_btn", "prev_btn", } + defaultFocusID = "next_btn" case PageInternalNetwork: componentOrder = []string{ - "InternalInterface_input", - "InternalIPAddress_input", - "InternalNetmask_input", + "PrivateInterface_input", + "PrivateIPAddress_input", + "PrivateNetmask_input", + "PrivateMTU_input", "next_btn", "prev_btn", } + defaultFocusID = "next_btn" case PageDNS: componentOrder = []string{ "Pri_DNS_input", @@ -308,14 +320,25 @@ func (m *model) initPageFocus(page PageType) { "next_btn", "prev_btn", } + defaultFocusID = "next_btn" case PageSummary: componentOrder = []string{"confirm_btn", "cancel_btn"} + defaultFocusID = "confirm_btn" } - // 注册组件到焦点管理器(按顺序) for _, compID := range componentOrder { if comp, exists := pageComps[compID]; exists { m.focusManager.Register(compID, comp) } } + + if defaultFocusID != "" { + if currentComp, exists := m.focusManager.GetCurrent(); exists { + currentComp.Blur() + } + if targetComp, exists := pageComps[defaultFocusID]; exists { + m.focusManager.currentFocusID = defaultFocusID + targetComp.Focus() + } + } } diff --git a/pkg/wizard/model.go b/pkg/wizard/model.go index 30798f8..57073b3 100644 --- a/pkg/wizard/model.go +++ b/pkg/wizard/model.go @@ -20,24 +20,26 @@ type Config struct { AgreementAccepted bool `json:"agreement_accepted"` // 数据接收 - Hostname string `json:"hostname"` - Country string `json:"country"` - Region string `json:"region"` - Timezone string `json:"timezone"` - HomePage string `json:"homepage"` - DBAddress string `json:"db_address"` - Software string `json:"software"` + ClusterName string `json:"cluster_name"` + Country string `json:"country"` + Region string `json:"region"` + Timezone string `json:"timezone"` + HomePage string `json:"homepage"` + DBAddress string `json:"db_address"` + Software string `json:"software"` // 公网设置 PublicInterface string `json:"public_interface"` PublicIPAddress string `json:"ip_address"` PublicNetmask string `json:"netmask"` PublicGateway string `json:"gateway"` + PublicMTU string `json:"public_mtu"` // 内网配置 - InternalInterface string `json:"internal_interface"` - InternalIPAddress string `json:"internal_ip"` - InternalNetmask string `json:"internal_mask"` + PrivateInterface string `json:"private_interface"` + PrivateIPAddress string `json:"private_ip"` + PrivateNetmask string `json:"private_mask"` + PrivateMTU string `json:"private_mtu"` // DNS 配置 DNSPrimary string `json:"dns_primary"` @@ -96,23 +98,24 @@ func defaultConfig() Config { } return Config{ - License: "This test license is for testing purposes only. Do not use it in production.", - Hostname: "cluster.hpc.org", - Country: "China", - Region: "Beijing", - Timezone: "Asia/Shanghai", - HomePage: "www.sunhpc.com", - DBAddress: "/var/lib/sunhpc/sunhpc.db", - Software: "/export/sunhpc", - PublicInterface: defaultPublicInterface, - PublicIPAddress: "", - PublicNetmask: "", - PublicGateway: "", - InternalInterface: defaultInternalInterface, - InternalIPAddress: "172.16.9.254", - InternalNetmask: "255.255.255.0", - DNSPrimary: "8.8.8.8", - DNSSecondary: "8.8.4.4", + License: "This test license is for testing purposes only. Do not use it in production.", + ClusterName: "cluster.hpc.org", + Country: "China", + Region: "Beijing", + Timezone: "Asia/Shanghai", + HomePage: "www.sunhpc.com", + DBAddress: "/var/lib/sunhpc/sunhpc.db", + Software: "/export/sunhpc", + PublicInterface: defaultPublicInterface, + PublicIPAddress: "", + PublicNetmask: "", + PublicGateway: "", + PrivateInterface: defaultInternalInterface, + PrivateIPAddress: "172.16.9.254", + PrivateNetmask: "255.255.255.0", + PrivateMTU: "1500", + DNSPrimary: "8.8.8.8", + DNSSecondary: "8.8.4.4", } } @@ -132,7 +135,7 @@ func initialModel() model { // ------------------ 页面2:基础信息页面 -------------------- page2Comps := make(map[string]Focusable) page2Comps["Homepage_input"] = NewTextInput("Homepage", cfg.HomePage) - page2Comps["Hostname_input"] = NewTextInput("Hostname", cfg.Hostname) + page2Comps["ClusterName_input"] = NewTextInput("ClusterName", cfg.ClusterName) page2Comps["Country_input"] = NewTextInput("Country", cfg.Country) page2Comps["Region_input"] = NewTextInput("Region", cfg.Region) page2Comps["Timezone_input"] = NewTextInput("Timezone", cfg.Timezone) @@ -148,15 +151,17 @@ func initialModel() model { page3Comps["PublicIPAddress_input"] = NewTextInput("PublicIPAddress", cfg.PublicIPAddress) page3Comps["PublicNetmask_input"] = NewTextInput("PublicNetmask", cfg.PublicNetmask) page3Comps["PublicGateway_input"] = NewTextInput("PublicGateway", cfg.PublicGateway) + page3Comps["PublicMTU_input"] = NewTextInput("PublicMTU", cfg.PublicMTU) page3Comps["next_btn"] = NewButton("下一步") page3Comps["prev_btn"] = NewButton("上一步") pageComponents[PagePublicNetwork] = page3Comps // ------------------ 页面4:内网网络页面 -------------------- page4Comps := make(map[string]Focusable) - page4Comps["InternalInterface_input"] = NewTextInput("InternalInterface", cfg.InternalInterface) - page4Comps["InternalIPAddress_input"] = NewTextInput("InternalIPAddress", cfg.InternalIPAddress) - page4Comps["InternalNetmask_input"] = NewTextInput("InternalNetmask", cfg.InternalNetmask) + page4Comps["PrivateInterface_input"] = NewTextInput("PrivateInterface", cfg.PrivateInterface) + page4Comps["PrivateIPAddress_input"] = NewTextInput("PrivateIPAddress", cfg.PrivateIPAddress) + page4Comps["PrivateNetmask_input"] = NewTextInput("PrivateNetmask", cfg.PrivateNetmask) + page4Comps["PrivateMTU_input"] = NewTextInput("PrivateMTU", cfg.PrivateMTU) page4Comps["next_btn"] = NewButton("下一步") page4Comps["prev_btn"] = NewButton("上一步") pageComponents[PageInternalNetwork] = page4Comps @@ -199,11 +204,13 @@ func (m model) Init() tea.Cmd { // Update 处理消息更新 func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { case tea.KeyMsg: switch msg.String() { case "ctrl+c": - m.quitting = true + m.saveConfig() + //m.quitting = true return m, tea.Quit // 1. 焦点切换(Tab/Shift+Tab)交给管理器处理 @@ -213,6 +220,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { // 2. 回车键:处理当前焦点组件的点击/确认 case "enter": + currentCompID := m.focusManager.currentFocusID switch currentCompID { // 页1:accept → 进入页2 @@ -232,7 +240,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { // 页6:确认配置 → 退出并保存 case "confirm_btn": m.done = true - m.quitting = true + //m.quitting = true return m, tea.Quit case "cancel_btn": @@ -258,8 +266,8 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { // 页2:基础信息 case "Homepage_input": m.config.HomePage = comp.Value() - case "Hostname_input": - m.config.Hostname = comp.Value() + case "ClusterName_input": + m.config.ClusterName = comp.Value() case "Country_input": m.config.Country = comp.Value() case "Region_input": @@ -280,14 +288,18 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.config.PublicNetmask = comp.Value() case "PublicGateway_input": m.config.PublicGateway = comp.Value() + case "PublicMTU_input": + m.config.PublicMTU = comp.Value() // 页4:内网网络 - case "InternalInterface_input": - m.config.InternalInterface = comp.Value() - case "InternalIPAddress_input": - m.config.InternalIPAddress = comp.Value() - case "InternalNetmask_input": - m.config.InternalNetmask = comp.Value() + case "PrivateInterface_input": + m.config.PrivateInterface = comp.Value() + case "PrivateIPAddress_input": + m.config.PrivateIPAddress = comp.Value() + case "PrivateNetmask_input": + m.config.PrivateNetmask = comp.Value() + case "PrivateMTU_input": + m.config.PrivateMTU = comp.Value() // 页5:DNS case "Pri_DNS_input": diff --git a/pkg/wizard/pages.go b/pkg/wizard/pages.go index 31e39ee..6ac0535 100644 --- a/pkg/wizard/pages.go +++ b/pkg/wizard/pages.go @@ -103,7 +103,7 @@ func renderDataInfoPage(m model) string { split_line, makeRow("Homepage", pageComps["Homepage_input"].View()), split_line, - makeRow("Hostname", pageComps["Hostname_input"].View()), + makeRow("ClusterName", pageComps["ClusterName_input"].View()), split_line, makeRow("Country", pageComps["Country_input"].View()), split_line, @@ -152,6 +152,8 @@ func renderPublicNetworkPage(m model) string { split_line, makeRow("PublicGateway", pageComps["PublicGateway_input"].View()), split_line, + makeRow("PublicMTU", pageComps["PublicMTU_input"].View()), + split_line, ) // 按钮区域 @@ -186,11 +188,13 @@ func renderInternalNetworkPage(m model) string { // 拼接内网网络表单 formContent := lipgloss.JoinVertical(lipgloss.Center, split_line, - makeRow("InternalInterface", pageComps["InternalInterface_input"].View()), + makeRow("PrivateInterface", pageComps["PrivateInterface_input"].View()), split_line, - makeRow("InternalIPAddress", pageComps["InternalIPAddress_input"].View()), + makeRow("PrivateIPAddress", pageComps["PrivateIPAddress_input"].View()), split_line, - makeRow("InternalNetmask", pageComps["InternalNetmask_input"].View()), + makeRow("PrivateNetmask", pageComps["PrivateNetmask_input"].View()), + split_line, + makeRow("PrivateMTU", pageComps["PrivateMTU_input"].View()), split_line, ) @@ -254,7 +258,7 @@ func renderSummaryPage(m model) string { // 拼接 Summary 表单 formContent := lipgloss.JoinVertical(lipgloss.Center, split_line, - makeRow("Hostname", m.config.Hostname), + makeRow("ClusterName", m.config.ClusterName), split_line, makeRow("Country", m.config.Country), split_line, @@ -276,11 +280,13 @@ func renderSummaryPage(m model) string { split_line, makeRow("PublicGateway", m.config.PublicGateway), split_line, - makeRow("InternalInterface", m.config.InternalInterface), + makeRow("PrivateInterface", m.config.PrivateInterface), split_line, - makeRow("InternalIPAddress", m.config.InternalIPAddress), + makeRow("PrivateIPAddress", m.config.PrivateIPAddress), split_line, - makeRow("InternalNetmask", m.config.InternalNetmask), + makeRow("PrivateNetmask", m.config.PrivateNetmask), + split_line, + makeRow("PrivateMTU", m.config.PrivateMTU), split_line, makeRow("Pri DNS", m.config.DNSPrimary), split_line,