diff --git a/cmd/edge-admin/build.sh b/cmd/edge-admin/build.sh deleted file mode 100755 index 41101fc6..00000000 --- a/cmd/edge-admin/build.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash - -env GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o edge-admin main.go \ No newline at end of file diff --git a/cmd/edge-admin/edge-admin b/cmd/edge-admin/edge-admin deleted file mode 100755 index 3706e2cb..00000000 Binary files a/cmd/edge-admin/edge-admin and /dev/null differ diff --git a/internal/configloaders/ui_config.go b/internal/configloaders/ui_config.go index 566a8b48..722296d0 100644 --- a/internal/configloaders/ui_config.go +++ b/internal/configloaders/ui_config.go @@ -9,13 +9,13 @@ import ( "reflect" ) -var sharedUIConfig *systemconfigs.UIConfig = nil +var sharedUIConfig *systemconfigs.AdminUIConfig = nil const ( UISettingName = "adminUIConfig" ) -func LoadUIConfig() (*systemconfigs.UIConfig, error) { +func LoadUIConfig() (*systemconfigs.AdminUIConfig, error) { locker.Lock() defer locker.Unlock() @@ -24,11 +24,11 @@ func LoadUIConfig() (*systemconfigs.UIConfig, error) { return nil, err } - v := reflect.Indirect(reflect.ValueOf(config)).Interface().(systemconfigs.UIConfig) + v := reflect.Indirect(reflect.ValueOf(config)).Interface().(systemconfigs.AdminUIConfig) return &v, nil } -func UpdateUIConfig(uiConfig *systemconfigs.UIConfig) error { +func UpdateUIConfig(uiConfig *systemconfigs.AdminUIConfig) error { locker.Lock() defer locker.Unlock() @@ -52,7 +52,7 @@ func UpdateUIConfig(uiConfig *systemconfigs.UIConfig) error { return nil } -func loadUIConfig() (*systemconfigs.UIConfig, error) { +func loadUIConfig() (*systemconfigs.AdminUIConfig, error) { if sharedUIConfig != nil { return sharedUIConfig, nil } @@ -71,7 +71,7 @@ func loadUIConfig() (*systemconfigs.UIConfig, error) { return sharedUIConfig, nil } - config := &systemconfigs.UIConfig{} + config := &systemconfigs.AdminUIConfig{} err = json.Unmarshal(resp.ValueJSON, config) if err != nil { logs.Println("[UI_MANAGER]" + err.Error()) @@ -82,8 +82,8 @@ func loadUIConfig() (*systemconfigs.UIConfig, error) { return sharedUIConfig, nil } -func defaultUIConfig() *systemconfigs.UIConfig { - return &systemconfigs.UIConfig{ +func defaultUIConfig() *systemconfigs.AdminUIConfig { + return &systemconfigs.AdminUIConfig{ ProductName: "GoEdge", AdminSystemName: "GoEdge管理员系统", ShowOpenSourceInfo: true, diff --git a/internal/nodes/admin_node.go b/internal/nodes/admin_node.go index a6c4bb39..d3ed0333 100644 --- a/internal/nodes/admin_node.go +++ b/internal/nodes/admin_node.go @@ -34,6 +34,10 @@ func (this *AdminNode) Run() { // 检查server配置 err := this.checkServer() if err != nil { + if err != nil { + logs.Println("[NODE]" + err.Error()) + return + } return } @@ -58,6 +62,7 @@ func (this *AdminNode) Run() { // 启动API节点 this.startAPINode() + // 启动Web服务 TeaGo.NewServer(false). AccessLog(false). EndAll(). @@ -129,7 +134,7 @@ func (this *AdminNode) startAPINode() { // 生成Secret func (this *AdminNode) genSecret() string { - tmpFile := os.TempDir() + "/edge-secret.tmp" + tmpFile := os.TempDir() + "/edge-admin-secret.tmp" data, err := ioutil.ReadFile(tmpFile) if err == nil && len(data) == 32 { return string(data) diff --git a/internal/rpc/rpc_client.go b/internal/rpc/rpc_client.go index 8d234bb1..3565250a 100644 --- a/internal/rpc/rpc_client.go +++ b/internal/rpc/rpc_client.go @@ -95,6 +95,10 @@ func (this *RPCClient) APINodeRPC() pb.APINodeServiceClient { return pb.NewAPINodeServiceClient(this.pickConn()) } +func (this *RPCClient) UserNodeRPC() pb.UserNodeServiceClient { + return pb.NewUserNodeServiceClient(this.pickConn()) +} + func (this *RPCClient) DBNodeRPC() pb.DBNodeServiceClient { return pb.NewDBNodeServiceClient(this.pickConn()) } diff --git a/internal/web/actions/default/api/init.go b/internal/web/actions/default/api/init.go index 06020c8c..03c74fae 100644 --- a/internal/web/actions/default/api/init.go +++ b/internal/web/actions/default/api/init.go @@ -13,7 +13,7 @@ func init() { server. Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeSetting)). Helper(NewHelper()). - Helper(settingutils.NewHelper("apiNodes")). + Helper(settingutils.NewAdvancedHelper("apiNodes")). Prefix("/api"). Get("", new(IndexAction)). GetPost("/node/createPopup", new(node.CreatePopupAction)). diff --git a/internal/web/actions/default/db/init.go b/internal/web/actions/default/db/init.go index 036f4bd0..7a2e1854 100644 --- a/internal/web/actions/default/db/init.go +++ b/internal/web/actions/default/db/init.go @@ -12,7 +12,7 @@ func init() { server. Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeSetting)). Helper(new(Helper)). - Helper(settingutils.NewHelper("dbNodes")). + Helper(settingutils.NewAdvancedHelper("dbNodes")). Prefix("/db"). Get("", new(IndexAction)). GetPost("/createPopup", new(CreatePopupAction)). diff --git a/internal/web/actions/default/settings/advanced.go b/internal/web/actions/default/settings/advanced.go new file mode 100644 index 00000000..acdf86a7 --- /dev/null +++ b/internal/web/actions/default/settings/advanced.go @@ -0,0 +1,16 @@ +package settings + +import "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" + +type AdvancedAction struct { + actionutils.ParentAction +} + +func (this *AdvancedAction) Init() { + this.Nav("", "", "") +} + +func (this *AdvancedAction) RunGet(params struct{}) { + // 跳转到高级设置的第一个Tab + this.RedirectURL("/settings/database") +} diff --git a/internal/web/actions/default/settings/database/init.go b/internal/web/actions/default/settings/database/init.go index 10fb9a30..7e2cb69b 100644 --- a/internal/web/actions/default/settings/database/init.go +++ b/internal/web/actions/default/settings/database/init.go @@ -11,7 +11,7 @@ func init() { TeaGo.BeforeStart(func(server *TeaGo.Server) { server. Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeSetting)). - Helper(settingutils.NewHelper("database")). + Helper(settingutils.NewAdvancedHelper("database")). Prefix("/settings/database"). Get("", new(IndexAction)). GetPost("/update", new(UpdateAction)). diff --git a/internal/web/actions/default/settings/init.go b/internal/web/actions/default/settings/init.go index a53634f5..cc8d8814 100644 --- a/internal/web/actions/default/settings/init.go +++ b/internal/web/actions/default/settings/init.go @@ -13,6 +13,7 @@ func init() { Helper(NewHelper()). Prefix("/settings"). Get("", new(IndexAction)). + Get("/advanced", new(AdvancedAction)). EndAll() }) } diff --git a/internal/web/actions/default/settings/settingutils/advanced_helper.go b/internal/web/actions/default/settings/settingutils/advanced_helper.go new file mode 100644 index 00000000..be7d0dc4 --- /dev/null +++ b/internal/web/actions/default/settings/settingutils/advanced_helper.go @@ -0,0 +1,41 @@ +package settingutils + +import ( + "github.com/TeaOSLab/EdgeAdmin/internal/configloaders" + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" + "github.com/iwind/TeaGo/actions" +) + +type AdvancedHelper struct { + tab string +} + +func NewAdvancedHelper(tab string) *AdvancedHelper { + return &AdvancedHelper{ + tab: tab, + } +} + +func (this *AdvancedHelper) BeforeAction(actionPtr actions.ActionWrapper) (goNext bool) { + goNext = true + + action := actionPtr.Object() + + // 左侧菜单 + action.Data["teaMenu"] = "settings" + action.Data["teaSubMenu"] = "advanced" + + // 标签栏 + tabbar := actionutils.NewTabbar() + var session = action.Session() + var adminId = session.GetInt64("adminId") + if configloaders.AllowModule(adminId, configloaders.AdminModuleCodeSetting) { + tabbar.Add("数据库", "", "/settings/database", "", this.tab == "database") + tabbar.Add("API节点", "", "/api", "", this.tab == "apiNodes") + tabbar.Add("用户节点", "", "/settings/userNodes", "", this.tab == "userNodes") + tabbar.Add("日志数据库", "", "/db", "", this.tab == "dbNodes") + } + actionutils.SetTabbar(actionPtr, tabbar) + + return +} diff --git a/internal/web/actions/default/settings/settingutils/utils.go b/internal/web/actions/default/settings/settingutils/helper.go similarity index 85% rename from internal/web/actions/default/settings/settingutils/utils.go rename to internal/web/actions/default/settings/settingutils/helper.go index 7d831c41..5718d8be 100644 --- a/internal/web/actions/default/settings/settingutils/utils.go +++ b/internal/web/actions/default/settings/settingutils/helper.go @@ -32,9 +32,6 @@ func (this *Helper) BeforeAction(actionPtr actions.ActionWrapper) (goNext bool) tabbar.Add("Web服务", "", "/settings/server", "", this.tab == "server") tabbar.Add("界面设置", "", "/settings/ui", "", this.tab == "ui") tabbar.Add("安全设置", "", "/settings/security", "", this.tab == "security") - tabbar.Add("数据库", "", "/settings/database", "", this.tab == "database") - tabbar.Add("API节点", "", "/api", "", this.tab == "apiNodes") - tabbar.Add("日志数据库", "", "/db", "", this.tab == "dbNodes") tabbar.Add("IP库", "", "/settings/ip-library", "", this.tab == "ipLibrary") tabbar.Add("备份", "", "/settings/backup", "", this.tab == "backup") } diff --git a/internal/web/actions/default/settings/user-nodes/delete.go b/internal/web/actions/default/settings/user-nodes/delete.go new file mode 100644 index 00000000..2dfd49ca --- /dev/null +++ b/internal/web/actions/default/settings/user-nodes/delete.go @@ -0,0 +1,28 @@ +package usernodes + +import ( + "github.com/TeaOSLab/EdgeAdmin/internal/oplogs" + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" + "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" +) + +type DeleteAction struct { + actionutils.ParentAction +} + +func (this *DeleteAction) RunPost(params struct { + NodeId int64 +}) { + // TODO 检查权限 + + _, err := this.RPC().UserNodeRPC().DeleteUserNode(this.AdminContext(), &pb.DeleteUserNodeRequest{NodeId: params.NodeId}) + if err != nil { + this.ErrorPage(err) + return + } + + // 创建日志 + defer this.CreateLog(oplogs.LevelInfo, "删除用户节点 %d", params.NodeId) + + this.Success() +} diff --git a/internal/web/actions/default/settings/user-nodes/helper.go b/internal/web/actions/default/settings/user-nodes/helper.go new file mode 100644 index 00000000..081a8eab --- /dev/null +++ b/internal/web/actions/default/settings/user-nodes/helper.go @@ -0,0 +1,15 @@ +package usernodes + +import ( + "github.com/iwind/TeaGo/actions" +) + +type Helper struct { +} + +func NewHelper() *Helper { + return &Helper{} +} + +func (this *Helper) BeforeAction(action *actions.ActionObject) { +} diff --git a/internal/web/actions/default/settings/user-nodes/index.go b/internal/web/actions/default/settings/user-nodes/index.go new file mode 100644 index 00000000..a0dfda1e --- /dev/null +++ b/internal/web/actions/default/settings/user-nodes/index.go @@ -0,0 +1,50 @@ +package usernodes + +import ( + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" + "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" + "github.com/iwind/TeaGo/maps" +) + +type IndexAction struct { + actionutils.ParentAction +} + +func (this *IndexAction) Init() { + this.Nav("", "node", "index") +} + +func (this *IndexAction) RunGet(params struct{}) { + countResp, err := this.RPC().UserNodeRPC().CountAllEnabledUserNodes(this.AdminContext(), &pb.CountAllEnabledUserNodesRequest{}) + if err != nil { + this.ErrorPage(err) + return + } + count := countResp.Count + page := this.NewPage(count) + this.Data["page"] = page.AsHTML() + + nodeMaps := []maps.Map{} + if count > 0 { + nodesResp, err := this.RPC().UserNodeRPC().ListEnabledUserNodes(this.AdminContext(), &pb.ListEnabledUserNodesRequest{ + Offset: page.Offset, + Size: page.Size, + }) + if err != nil { + this.ErrorPage(err) + return + } + + for _, node := range nodesResp.Nodes { + nodeMaps = append(nodeMaps, maps.Map{ + "id": node.Id, + "isOn": node.IsOn, + "name": node.Name, + "accessAddrs": node.AccessAddrs, + }) + } + } + this.Data["nodes"] = nodeMaps + + this.Show() +} diff --git a/internal/web/actions/default/settings/user-nodes/init.go b/internal/web/actions/default/settings/user-nodes/init.go new file mode 100644 index 00000000..e60aa625 --- /dev/null +++ b/internal/web/actions/default/settings/user-nodes/init.go @@ -0,0 +1,23 @@ +package usernodes + +import ( + "github.com/TeaOSLab/EdgeAdmin/internal/configloaders" + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/settingutils" + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/user-nodes/node" + "github.com/TeaOSLab/EdgeAdmin/internal/web/helpers" + "github.com/iwind/TeaGo" +) + +func init() { + TeaGo.BeforeStart(func(server *TeaGo.Server) { + server. + Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeSetting)). + Helper(NewHelper()). + Helper(settingutils.NewAdvancedHelper("userNodes")). + Prefix("/settings/userNodes"). + Get("", new(IndexAction)). + GetPost("/node/createPopup", new(node.CreatePopupAction)). + Post("/delete", new(DeleteAction)). + EndAll() + }) +} diff --git a/internal/web/actions/default/settings/user-nodes/node/createAddrPopup.go b/internal/web/actions/default/settings/user-nodes/node/createAddrPopup.go new file mode 100644 index 00000000..75a61c9d --- /dev/null +++ b/internal/web/actions/default/settings/user-nodes/node/createAddrPopup.go @@ -0,0 +1,46 @@ +package node + +import ( + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" + "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs" + "github.com/iwind/TeaGo/actions" + "net" +) + +// 添加地址 +type CreateAddrPopupAction struct { + actionutils.ParentAction +} + +func (this *CreateAddrPopupAction) Init() { + this.Nav("", "", "") +} + +func (this *CreateAddrPopupAction) RunGet(params struct { +}) { + this.Show() +} + +func (this *CreateAddrPopupAction) RunPost(params struct { + Protocol string + Addr string + Must *actions.Must +}) { + params.Must. + Field("addr", params.Addr). + Require("请输入访问地址") + + + host, port, err := net.SplitHostPort(params.Addr) + if err != nil { + this.FailField("addr", "错误的访问地址") + } + + addrConfig := &serverconfigs.NetworkAddressConfig{ + Protocol: serverconfigs.Protocol(params.Protocol), + Host: host, + PortRange: port, + } + this.Data["addr"] = addrConfig + this.Success() +} diff --git a/internal/web/actions/default/settings/user-nodes/node/createPopup.go b/internal/web/actions/default/settings/user-nodes/node/createPopup.go new file mode 100644 index 00000000..76146278 --- /dev/null +++ b/internal/web/actions/default/settings/user-nodes/node/createPopup.go @@ -0,0 +1,143 @@ +package node + +import ( + "encoding/json" + "github.com/TeaOSLab/EdgeAdmin/internal/oplogs" + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" + "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" + "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs" + "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs" + "github.com/iwind/TeaGo/actions" +) + +type CreatePopupAction struct { + actionutils.ParentAction +} + +func (this *CreatePopupAction) Init() { + this.Nav("", "node", "create") +} + +func (this *CreatePopupAction) RunGet(params struct{}) { + this.Show() +} + +func (this *CreatePopupAction) RunPost(params struct { + Name string + Description string + ListensJSON []byte + CertIdsJSON []byte + AccessAddrsJSON []byte + IsOn bool + + Must *actions.Must +}) { + params.Must. + Field("name", params.Name). + Require("请输入用户节点名称") + + httpConfig := &serverconfigs.HTTPProtocolConfig{} + httpsConfig := &serverconfigs.HTTPSProtocolConfig{} + + // 监听地址 + listens := []*serverconfigs.NetworkAddressConfig{} + err := json.Unmarshal(params.ListensJSON, &listens) + if err != nil { + this.ErrorPage(err) + return + } + if len(listens) == 0 { + this.Fail("请添加至少一个进程监听地址") + } + for _, addr := range listens { + if addr.Protocol.IsHTTPFamily() { + httpConfig.IsOn = true + httpConfig.Listen = append(httpConfig.Listen, addr) + } else if addr.Protocol.IsHTTPSFamily() { + httpsConfig.IsOn = true + httpsConfig.Listen = append(httpsConfig.Listen, addr) + } + } + + // 证书 + certIds := []int64{} + if len(params.CertIdsJSON) > 0 { + err = json.Unmarshal(params.CertIdsJSON, &certIds) + if err != nil { + this.ErrorPage(err) + return + } + } + if httpsConfig.IsOn && len(httpsConfig.Listen) > 0 && len(certIds) == 0 { + this.Fail("请添加至少一个证书") + } + + certRefs := []*sslconfigs.SSLCertRef{} + for _, certId := range certIds { + certRefs = append(certRefs, &sslconfigs.SSLCertRef{ + IsOn: true, + CertId: certId, + }) + } + certRefsJSON, err := json.Marshal(certRefs) + if err != nil { + this.ErrorPage(err) + return + } + + // 创建策略 + if len(certIds) > 0 { + sslPolicyCreateResp, err := this.RPC().SSLPolicyRPC().CreateSSLPolicy(this.AdminContext(), &pb.CreateSSLPolicyRequest{ + CertsJSON: certRefsJSON, + }) + if err != nil { + this.ErrorPage(err) + return + } + sslPolicyId := sslPolicyCreateResp.SslPolicyId + httpsConfig.SSLPolicyRef = &sslconfigs.SSLPolicyRef{ + IsOn: true, + SSLPolicyId: sslPolicyId, + } + } + + // 访问地址 + accessAddrs := []*serverconfigs.NetworkAddressConfig{} + err = json.Unmarshal(params.AccessAddrsJSON, &accessAddrs) + if err != nil { + this.ErrorPage(err) + return + } + if len(accessAddrs) == 0 { + this.Fail("请添加至少一个外部访问地址") + } + + httpJSON, err := json.Marshal(httpConfig) + if err != nil { + this.ErrorPage(err) + return + } + httpsJSON, err := json.Marshal(httpsConfig) + if err != nil { + this.ErrorPage(err) + return + } + + createResp, err := this.RPC().UserNodeRPC().CreateUserNode(this.AdminContext(), &pb.CreateUserNodeRequest{ + Name: params.Name, + Description: params.Description, + HttpJSON: httpJSON, + HttpsJSON: httpsJSON, + AccessAddrsJSON: params.AccessAddrsJSON, + IsOn: params.IsOn, + }) + if err != nil { + this.ErrorPage(err) + return + } + + // 创建日志 + defer this.CreateLog(oplogs.LevelInfo, "创建用户节点 %d", createResp.NodeId) + + this.Success() +} diff --git a/internal/web/actions/default/settings/user-nodes/node/helper.go b/internal/web/actions/default/settings/user-nodes/node/helper.go new file mode 100644 index 00000000..689a22cc --- /dev/null +++ b/internal/web/actions/default/settings/user-nodes/node/helper.go @@ -0,0 +1,21 @@ +package node + +import ( + "github.com/iwind/TeaGo/actions" + "net/http" +) + +type Helper struct { +} + +func NewHelper() *Helper { + return &Helper{} +} + +func (this *Helper) BeforeAction(action *actions.ActionObject) (goNext bool) { + if action.Request.Method != http.MethodGet { + return true + } + + return true +} diff --git a/internal/web/actions/default/settings/user-nodes/node/index.go b/internal/web/actions/default/settings/user-nodes/node/index.go new file mode 100644 index 00000000..ab79fce3 --- /dev/null +++ b/internal/web/actions/default/settings/user-nodes/node/index.go @@ -0,0 +1,102 @@ +package node + +import ( + "encoding/json" + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" + "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" + "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs" + "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs" + "github.com/iwind/TeaGo/maps" +) + +type IndexAction struct { + actionutils.ParentAction +} + +func (this *IndexAction) Init() { + this.Nav("", "", "index") +} + +func (this *IndexAction) RunGet(params struct { + NodeId int64 +}) { + nodeResp, err := this.RPC().UserNodeRPC().FindEnabledUserNode(this.AdminContext(), &pb.FindEnabledUserNodeRequest{NodeId: params.NodeId}) + if err != nil { + this.ErrorPage(err) + return + } + node := nodeResp.Node + if node == nil { + this.NotFound("userNode", params.NodeId) + return + } + + // 监听地址 + httpConfig := &serverconfigs.HTTPProtocolConfig{} + if len(node.HttpJSON) > 0 { + err = json.Unmarshal(node.HttpJSON, httpConfig) + if err != nil { + this.ErrorPage(err) + return + } + } + httpsConfig := &serverconfigs.HTTPSProtocolConfig{} + if len(node.HttpsJSON) > 0 { + err = json.Unmarshal(node.HttpsJSON, httpsConfig) + if err != nil { + this.ErrorPage(err) + return + } + } + + // 监听地址 + listens := []*serverconfigs.NetworkAddressConfig{} + listens = append(listens, httpConfig.Listen...) + listens = append(listens, httpsConfig.Listen...) + + // 证书信息 + certs := []*sslconfigs.SSLCertConfig{} + sslPolicyId := int64(0) + if httpsConfig.SSLPolicyRef != nil && httpsConfig.SSLPolicyRef.SSLPolicyId > 0 { + sslPolicyConfigResp, err := this.RPC().SSLPolicyRPC().FindEnabledSSLPolicyConfig(this.AdminContext(), &pb.FindEnabledSSLPolicyConfigRequest{SslPolicyId: httpsConfig.SSLPolicyRef.SSLPolicyId}) + if err != nil { + this.ErrorPage(err) + return + } + sslPolicyConfigJSON := sslPolicyConfigResp.SslPolicyJSON + if len(sslPolicyConfigJSON) > 0 { + sslPolicyId = httpsConfig.SSLPolicyRef.SSLPolicyId + + sslPolicy := &sslconfigs.SSLPolicy{} + err = json.Unmarshal(sslPolicyConfigJSON, sslPolicy) + if err != nil { + this.ErrorPage(err) + return + } + certs = sslPolicy.Certs + } + } + + // 访问地址 + accessAddrs := []*serverconfigs.NetworkAddressConfig{} + if len(node.AccessAddrsJSON) > 0 { + err = json.Unmarshal(node.AccessAddrsJSON, &accessAddrs) + if err != nil { + this.ErrorPage(err) + return + } + } + + this.Data["node"] = maps.Map{ + "id": node.Id, + "name": node.Name, + "description": node.Description, + "isOn": node.IsOn, + "listens": listens, + "accessAddrs": accessAddrs, + "hasHTTPS": sslPolicyId > 0, + "certs": certs, + } + + this.Show() +} diff --git a/internal/web/actions/default/settings/user-nodes/node/init.go b/internal/web/actions/default/settings/user-nodes/node/init.go new file mode 100644 index 00000000..398b6293 --- /dev/null +++ b/internal/web/actions/default/settings/user-nodes/node/init.go @@ -0,0 +1,29 @@ +package node + +import ( + "github.com/TeaOSLab/EdgeAdmin/internal/configloaders" + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/settingutils" + "github.com/TeaOSLab/EdgeAdmin/internal/web/helpers" + "github.com/iwind/TeaGo" +) + +func init() { + TeaGo.BeforeStart(func(server *TeaGo.Server) { + server. + Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeSetting)). + Helper(settingutils.NewAdvancedHelper("userNodes")). + Prefix("/settings/userNodes/node"). + + // 这里不受Helper的约束 + GetPost("/createAddrPopup", new(CreateAddrPopupAction)). + GetPost("/updateAddrPopup", new(UpdateAddrPopupAction)). + + // 节点相关 + Helper(NewHelper()). + Get("", new(IndexAction)). + GetPost("/update", new(UpdateAction)). + Get("/install", new(InstallAction)). + + EndAll() + }) +} diff --git a/internal/web/actions/default/settings/user-nodes/node/install.go b/internal/web/actions/default/settings/user-nodes/node/install.go new file mode 100644 index 00000000..bae78738 --- /dev/null +++ b/internal/web/actions/default/settings/user-nodes/node/install.go @@ -0,0 +1,57 @@ +package node + +import ( + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" + "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" + "github.com/iwind/TeaGo/maps" + "strings" +) + +type InstallAction struct { + actionutils.ParentAction +} + +func (this *InstallAction) Init() { + this.Nav("", "", "install") +} + +func (this *InstallAction) RunGet(params struct { + NodeId int64 +}) { + // 用户节点信息 + nodeResp, err := this.RPC().UserNodeRPC().FindEnabledUserNode(this.AdminContext(), &pb.FindEnabledUserNodeRequest{NodeId: params.NodeId}) + if err != nil { + this.ErrorPage(err) + return + } + node := nodeResp.Node + if node == nil { + this.NotFound("userNode", params.NodeId) + return + } + + this.Data["node"] = maps.Map{ + "id": node.Id, + "name": node.Name, + "uniqueId": node.UniqueId, + "secret": node.Secret, + } + + // API节点列表 + apiNodesResp, err := this.RPC().APINodeRPC().FindAllEnabledAPINodes(this.AdminContext(), &pb.FindAllEnabledAPINodesRequest{}) + if err != nil { + this.ErrorPage(err) + return + } + apiNodes := apiNodesResp.Nodes + apiEndpoints := []string{} + for _, apiNode := range apiNodes { + if !apiNode.IsOn { + continue + } + apiEndpoints = append(apiEndpoints, apiNode.AccessAddrs...) + } + this.Data["apiEndpoints"] = "\"" + strings.Join(apiEndpoints, "\", \"") + "\"" + + this.Show() +} diff --git a/internal/web/actions/default/settings/user-nodes/node/update.go b/internal/web/actions/default/settings/user-nodes/node/update.go new file mode 100644 index 00000000..c2eca626 --- /dev/null +++ b/internal/web/actions/default/settings/user-nodes/node/update.go @@ -0,0 +1,240 @@ +package node + +import ( + "encoding/json" + "github.com/TeaOSLab/EdgeAdmin/internal/oplogs" + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" + "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" + "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs" + "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs" + "github.com/iwind/TeaGo/actions" + "github.com/iwind/TeaGo/maps" +) + +type UpdateAction struct { + actionutils.ParentAction +} + +func (this *UpdateAction) Init() { + this.Nav("", "", "update") +} + +func (this *UpdateAction) RunGet(params struct { + NodeId int64 +}) { + nodeResp, err := this.RPC().UserNodeRPC().FindEnabledUserNode(this.AdminContext(), &pb.FindEnabledUserNodeRequest{ + NodeId: params.NodeId, + }) + if err != nil { + this.ErrorPage(err) + return + } + node := nodeResp.Node + if node == nil { + this.WriteString("要操作的节点不存在") + return + } + + httpConfig := &serverconfigs.HTTPProtocolConfig{} + if len(node.HttpJSON) > 0 { + err = json.Unmarshal(node.HttpJSON, httpConfig) + if err != nil { + this.ErrorPage(err) + return + } + } + httpsConfig := &serverconfigs.HTTPSProtocolConfig{} + if len(node.HttpsJSON) > 0 { + err = json.Unmarshal(node.HttpsJSON, httpsConfig) + if err != nil { + this.ErrorPage(err) + return + } + } + + // 监听地址 + listens := []*serverconfigs.NetworkAddressConfig{} + listens = append(listens, httpConfig.Listen...) + listens = append(listens, httpsConfig.Listen...) + + // 证书信息 + certs := []*sslconfigs.SSLCertConfig{} + sslPolicyId := int64(0) + if httpsConfig.SSLPolicyRef != nil && httpsConfig.SSLPolicyRef.SSLPolicyId > 0 { + sslPolicyConfigResp, err := this.RPC().SSLPolicyRPC().FindEnabledSSLPolicyConfig(this.AdminContext(), &pb.FindEnabledSSLPolicyConfigRequest{SslPolicyId: httpsConfig.SSLPolicyRef.SSLPolicyId}) + if err != nil { + this.ErrorPage(err) + return + } + sslPolicyConfigJSON := sslPolicyConfigResp.SslPolicyJSON + if len(sslPolicyConfigJSON) > 0 { + sslPolicyId = httpsConfig.SSLPolicyRef.SSLPolicyId + + sslPolicy := &sslconfigs.SSLPolicy{} + err = json.Unmarshal(sslPolicyConfigJSON, sslPolicy) + if err != nil { + this.ErrorPage(err) + return + } + certs = sslPolicy.Certs + } + } + + accessAddrs := []*serverconfigs.NetworkAddressConfig{} + if len(node.AccessAddrsJSON) > 0 { + err = json.Unmarshal(node.AccessAddrsJSON, &accessAddrs) + if err != nil { + this.ErrorPage(err) + return + } + } + + this.Data["node"] = maps.Map{ + "id": node.Id, + "name": node.Name, + "description": node.Description, + "isOn": node.IsOn, + "listens": listens, + "certs": certs, + "sslPolicyId": sslPolicyId, + "accessAddrs": accessAddrs, + } + + this.Show() +} + +// 保存基础设置 +func (this *UpdateAction) RunPost(params struct { + NodeId int64 + Name string + SslPolicyId int64 + ListensJSON []byte + CertIdsJSON []byte + AccessAddrsJSON []byte + Description string + IsOn bool + + Must *actions.Must +}) { + params.Must. + Field("name", params.Name). + Require("请输入用户节点名称") + + httpConfig := &serverconfigs.HTTPProtocolConfig{} + httpsConfig := &serverconfigs.HTTPSProtocolConfig{} + + // 监听地址 + listens := []*serverconfigs.NetworkAddressConfig{} + err := json.Unmarshal(params.ListensJSON, &listens) + if err != nil { + this.ErrorPage(err) + return + } + if len(listens) == 0 { + this.Fail("请添加至少一个进程监听地址") + } + for _, addr := range listens { + if addr.Protocol.IsHTTPFamily() { + httpConfig.IsOn = true + httpConfig.Listen = append(httpConfig.Listen, addr) + } else if addr.Protocol.IsHTTPSFamily() { + httpsConfig.IsOn = true + httpsConfig.Listen = append(httpsConfig.Listen, addr) + } + } + + // 证书 + certIds := []int64{} + if len(params.CertIdsJSON) > 0 { + err = json.Unmarshal(params.CertIdsJSON, &certIds) + if err != nil { + this.ErrorPage(err) + return + } + } + if httpsConfig.IsOn && len(httpsConfig.Listen) > 0 && len(certIds) == 0 { + this.Fail("请添加至少一个证书") + } + + certRefs := []*sslconfigs.SSLCertRef{} + for _, certId := range certIds { + certRefs = append(certRefs, &sslconfigs.SSLCertRef{ + IsOn: true, + CertId: certId, + }) + } + certRefsJSON, err := json.Marshal(certRefs) + if err != nil { + this.ErrorPage(err) + return + } + + // 创建策略 + sslPolicyId := params.SslPolicyId + if sslPolicyId == 0 { + if len(certIds) > 0 { + sslPolicyCreateResp, err := this.RPC().SSLPolicyRPC().CreateSSLPolicy(this.AdminContext(), &pb.CreateSSLPolicyRequest{ + CertsJSON: certRefsJSON, + }) + if err != nil { + this.ErrorPage(err) + return + } + sslPolicyId = sslPolicyCreateResp.SslPolicyId + } + } else { + _, err = this.RPC().SSLPolicyRPC().UpdateSSLPolicy(this.AdminContext(), &pb.UpdateSSLPolicyRequest{ + SslPolicyId: sslPolicyId, + CertsJSON: certRefsJSON, + }) + if err != nil { + this.ErrorPage(err) + return + } + } + httpsConfig.SSLPolicyRef = &sslconfigs.SSLPolicyRef{ + IsOn: true, + SSLPolicyId: sslPolicyId, + } + + // 访问地址 + accessAddrs := []*serverconfigs.NetworkAddressConfig{} + err = json.Unmarshal(params.AccessAddrsJSON, &accessAddrs) + if err != nil { + this.ErrorPage(err) + return + } + if len(accessAddrs) == 0 { + this.Fail("请添加至少一个外部访问地址") + } + + httpJSON, err := json.Marshal(httpConfig) + if err != nil { + this.ErrorPage(err) + return + } + httpsJSON, err := json.Marshal(httpsConfig) + if err != nil { + this.ErrorPage(err) + return + } + + _, err = this.RPC().UserNodeRPC().UpdateUserNode(this.AdminContext(), &pb.UpdateUserNodeRequest{ + NodeId: params.NodeId, + Name: params.Name, + Description: params.Description, + HttpJSON: httpJSON, + HttpsJSON: httpsJSON, + AccessAddrsJSON: params.AccessAddrsJSON, + IsOn: params.IsOn, + }) + if err != nil { + this.ErrorPage(err) + return + } + + // 创建日志 + defer this.CreateLog(oplogs.LevelInfo, "修改用户节点 %d", params.NodeId) + + this.Success() +} diff --git a/internal/web/actions/default/settings/user-nodes/node/updateAddrPopup.go b/internal/web/actions/default/settings/user-nodes/node/updateAddrPopup.go new file mode 100644 index 00000000..539270f8 --- /dev/null +++ b/internal/web/actions/default/settings/user-nodes/node/updateAddrPopup.go @@ -0,0 +1,42 @@ +package node + +import ( + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" + "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs" + "github.com/iwind/TeaGo/actions" + "net" +) + +type UpdateAddrPopupAction struct { + actionutils.ParentAction +} + +func (this *UpdateAddrPopupAction) Init() { + this.Nav("", "", "") +} + +func (this *UpdateAddrPopupAction) RunGet(params struct{}) { + this.Show() +} + +func (this *UpdateAddrPopupAction) RunPost(params struct { + Protocol string + Addr string + Must *actions.Must +}) { + params.Must. + Field("addr", params.Addr). + Require("请输入访问地址") + host, port, err := net.SplitHostPort(params.Addr) + if err != nil { + this.FailField("addr", "错误的访问地址") + } + + addrConfig := &serverconfigs.NetworkAddressConfig{ + Protocol: serverconfigs.Protocol(params.Protocol), + Host: host, + PortRange: port, + } + this.Data["addr"] = addrConfig + this.Success() +} diff --git a/internal/web/helpers/user_must_auth.go b/internal/web/helpers/user_must_auth.go index a7ae8503..85b51efd 100644 --- a/internal/web/helpers/user_must_auth.go +++ b/internal/web/helpers/user_must_auth.go @@ -226,6 +226,13 @@ func (this *userMustAuth) modules(adminId int64) []maps.Map { "module": configloaders.AdminModuleCodeSetting, "name": "系统设置", "icon": "setting", + "subItems": []maps.Map{ + { + "name": "高级设置", + "url": "/settings/advanced", + "code": "advanced", + }, + }, }, } diff --git a/internal/web/import.go b/internal/web/import.go index af0dc8e4..c71aeef3 100644 --- a/internal/web/import.go +++ b/internal/web/import.go @@ -85,6 +85,7 @@ import ( _ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/server" _ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/ui" _ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/upgrade" + _ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/user-nodes" _ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/setup" _ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/ui" _ "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/users" diff --git a/web/public/js/components/common/network-addresses-view.js b/web/public/js/components/common/network-addresses-view.js index 4819d7da..e650984c 100644 --- a/web/public/js/components/common/network-addresses-view.js +++ b/web/public/js/components/common/network-addresses-view.js @@ -2,7 +2,7 @@ Vue.component("network-addresses-view", { props: ["v-addresses"], template: `
暂时还没有节点。
+ +| 节点名称 | +访问地址 | +状态 | +操作 | +
|---|---|---|---|
| {{node.name}} | +
+
+ {{addr}}
+
+ |
+
+ |
+ + 详情 + 删除 + | +
| 节点名称 | ++ {{node.name}} + | +
| 状态 | +
+ |
+
| 进程监听端口 | +
+ 用户节点进程监听的网络端口。 + |
+
| HTTPS证书 | +
+ |
+
| 外部访问地址 | +
+ 可以公开访问的网络地址。 + |
+
| 描述 | ++ {{node.description}} + 暂时还没有描述。 + | +
| configs/api.yaml |
+
+ rpc:
+ endpoints: [ {{apiEndpoints}} ]
+nodeId: "{{node.uniqueId}}"
+secret: "{{node.secret}}"
+ |
+