diff --git a/internal/web/actions/default/clusters/cluster/createNode.go b/internal/web/actions/default/clusters/cluster/createNode.go index 4e66fa31..8f4fe246 100644 --- a/internal/web/actions/default/clusters/cluster/createNode.go +++ b/internal/web/actions/default/clusters/cluster/createNode.go @@ -5,6 +5,7 @@ import ( "github.com/TeaOSLab/EdgeAdmin/internal/oplogs" "github.com/TeaOSLab/EdgeAdmin/internal/utils" "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/clusterutils" "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/grants/grantutils" "github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs" "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" @@ -27,7 +28,7 @@ func (this *CreateNodeAction) Init() { func (this *CreateNodeAction) RunGet(params struct { ClusterId int64 }) { - leftMenuItems := []maps.Map{ + var leftMenuItems = []maps.Map{ { "name": "单个创建", "url": "/clusters/cluster/createNode?clusterId=" + strconv.FormatInt(params.ClusterId, 10), @@ -47,7 +48,7 @@ func (this *CreateNodeAction) RunGet(params struct { this.ErrorPage(err) return } - dnsRouteMaps := []maps.Map{} + var dnsRouteMaps = []maps.Map{} this.Data["dnsDomainId"] = 0 if clusterDNSResp.Domain != nil { domainId := clusterDNSResp.Domain.Id @@ -76,8 +77,8 @@ func (this *CreateNodeAction) RunGet(params struct { this.ErrorPage(err) return } - apiNodes := apiNodesResp.ApiNodes - apiEndpoints := []string{} + var apiNodes = apiNodesResp.ApiNodes + var apiEndpoints = []string{} for _, apiNode := range apiNodes { if !apiNode.IsOn { continue @@ -86,6 +87,9 @@ func (this *CreateNodeAction) RunGet(params struct { } this.Data["apiEndpoints"] = "\"" + strings.Join(apiEndpoints, "\", \"") + "\"" + // 安装文件下载 + this.Data["installerFiles"] = clusterutils.ListInstallerFiles() + this.Show() } diff --git a/internal/web/actions/default/clusters/cluster/downloadInstaller.go b/internal/web/actions/default/clusters/cluster/downloadInstaller.go new file mode 100644 index 00000000..c70e8a84 --- /dev/null +++ b/internal/web/actions/default/clusters/cluster/downloadInstaller.go @@ -0,0 +1,70 @@ +// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn . + +package cluster + +import ( + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" + "github.com/iwind/TeaGo/Tea" + "github.com/iwind/TeaGo/types" + "io" + "net/http" + "os" + "regexp" +) + +type DownloadInstallerAction struct { + actionutils.ParentAction +} + +func (this *DownloadInstallerAction) Init() { + this.Nav("", "", "") +} + +func (this *DownloadInstallerAction) RunGet(params struct { + Name string +}) { + if len(params.Name) == 0 { + this.ResponseWriter.WriteHeader(http.StatusNotFound) + this.WriteString("file not found") + return + } + + // 检查文件名 + // 以防止路径穿越等风险 + if !regexp.MustCompile(`^[a-zA-Z0-9.-]+$`).MatchString(params.Name) { + this.ResponseWriter.WriteHeader(http.StatusNotFound) + this.WriteString("file not found") + return + } + + var zipFile = Tea.Root + "/edge-api/deploy/" + params.Name + fp, err := os.OpenFile(zipFile, os.O_RDWR, 0444) + if err != nil { + if os.IsNotExist(err) { + this.ResponseWriter.WriteHeader(http.StatusNotFound) + this.WriteString("file not found") + return + } + + this.ResponseWriter.WriteHeader(http.StatusInternalServerError) + this.WriteString("file can not be opened") + + return + } + + defer func() { + _ = fp.Close() + }() + + stat, err := fp.Stat() + if err != nil { + this.ResponseWriter.WriteHeader(http.StatusInternalServerError) + this.WriteString("file can not be opened") + return + } + + this.AddHeader("Content-Disposition", "attachment; filename=\""+params.Name+"\";") + this.AddHeader("Content-Type", "application/zip") + this.AddHeader("Content-Length", types.String(stat.Size())) + _, _ = io.Copy(this.ResponseWriter, fp) +} diff --git a/internal/web/actions/default/clusters/cluster/init.go b/internal/web/actions/default/clusters/cluster/init.go index e70ca33f..4586ecaa 100644 --- a/internal/web/actions/default/clusters/cluster/init.go +++ b/internal/web/actions/default/clusters/cluster/init.go @@ -35,6 +35,7 @@ func init() { GetPost("/updateNodeSSH", new(UpdateNodeSSHAction)). GetPost("/installManual", new(InstallManualAction)). Post("/suggestLoginPorts", new(SuggestLoginPortsAction)). + Get("/downloadInstaller", new(DownloadInstallerAction)). // 节点相关 Prefix("/clusters/cluster/node"). diff --git a/internal/web/actions/default/clusters/cluster/node/install.go b/internal/web/actions/default/clusters/cluster/node/install.go index d4779b72..1372dde9 100644 --- a/internal/web/actions/default/clusters/cluster/node/install.go +++ b/internal/web/actions/default/clusters/cluster/node/install.go @@ -1,12 +1,16 @@ package node import ( + "encoding/json" "github.com/TeaOSLab/EdgeAdmin/internal/oplogs" "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils" + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/clusterutils" + "github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs" "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" "github.com/iwind/TeaGo/actions" "github.com/iwind/TeaGo/maps" + "path/filepath" "strings" ) @@ -32,6 +36,20 @@ func (this *InstallAction) RunGet(params struct { return } + // 最近运行目录 + var exeRoot = "" + if len(node.StatusJSON) > 0 { + var nodeStatus = &nodeconfigs.NodeStatus{} + err = json.Unmarshal(node.StatusJSON, nodeStatus) + if err == nil { + var exePath = nodeStatus.ExePath + if len(exePath) > 0 { + exeRoot = filepath.Dir(filepath.Dir(exePath)) + } + } + } + this.Data["exeRoot"] = exeRoot + // 安装信息 if node.InstallStatus != nil { this.Data["installStatus"] = maps.Map{ @@ -70,7 +88,7 @@ func (this *InstallAction) RunGet(params struct { this.ErrorPage(err) return } - apiNodes := apiNodesResp.ApiNodes + var apiNodes = apiNodesResp.ApiNodes apiEndpoints := []string{} for _, apiNode := range apiNodes { if !apiNode.IsOn { @@ -87,6 +105,10 @@ func (this *InstallAction) RunGet(params struct { nodeMap["secret"] = node.Secret nodeMap["cluster"] = clusterMap + // 安装文件 + var installerFiles = clusterutils.ListInstallerFiles() + this.Data["installerFiles"] = installerFiles + this.Show() } diff --git a/internal/web/actions/default/clusters/clusterutils/installer.go b/internal/web/actions/default/clusters/clusterutils/installer.go new file mode 100644 index 00000000..27f399f8 --- /dev/null +++ b/internal/web/actions/default/clusters/clusterutils/installer.go @@ -0,0 +1,66 @@ +// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn . + +package clusterutils + +import ( + teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const" + "github.com/iwind/TeaGo/Tea" + "path/filepath" + "regexp" + "sort" + "strings" +) + +type installerFile struct { + Name string `json:"name"` + OS string `json:"os"` + Arch string `json:"arch"` + Version string `json:"version"` +} + +func ListInstallerFiles() []*installerFile { + var dir = Tea.Root + "/edge-api/deploy" + matches, err := filepath.Glob(dir + "/edge-node-*.zip") + if err != nil { + return nil + } + + var result = []*installerFile{} + var reg = regexp.MustCompile(`^edge-node-(\w+)-(\w+)-v([\w.]+)\.zip$`) + for _, match := range matches { + var baseName = filepath.Base(match) + var subMatches = reg.FindStringSubmatch(baseName) + if len(subMatches) >= 4 { + var osName = subMatches[1] + if len(osName) > 0 { + osName = strings.ToUpper(osName[:1]) + osName[1:] + } + + var arch = subMatches[2] + if arch == "amd64" { + arch = "x86_64" + } + + var version = subMatches[3] + if version != teaconst.Version { // 只能下载当前版本 + continue + } + + result = append(result, &installerFile{ + Name: subMatches[0], + OS: osName, + Arch: arch, + Version: version, + }) + } + } + + // 排序,将x86_64排在最上面 + if len(result) > 0 { + sort.Slice(result, func(i, j int) bool { + return result[i].Arch == "x86_64" + }) + } + + return result +} diff --git a/web/views/@default/clusters/cluster/createNode.html b/web/views/@default/clusters/cluster/createNode.html index 2d905e0e..7fcd3f80 100644 --- a/web/views/@default/clusters/cluster/createNode.html +++ b/web/views/@default/clusters/cluster/createNode.html @@ -140,7 +140,7 @@
| 文件名 | +操作系统 | +CPU架构 | +版本 | +
|---|---|---|---|
| + {{installerFile.name}} + | +{{installerFile.os}} | +{{installerFile.arch}} | +v{{installerFile.version}} | +
| 安装文件 | +
+
|
+ ||||||||
| 配置文件(configs/api.yaml) |
@@ -37,8 +60,9 @@ secret: "{{node.secret}}"
|||||||||
| 安装目录 |
- 使用集群设置({{node.cluster.installDir}})
+
+ 建议使用集群设置({{node.cluster.installDir}}
+ 建议安装在 /usr/local/goedge/edge-node
{{node.installDir}}
|
@@ -71,6 +95,10 @@ secret: "{{node.secret}}"
{{node.installDir}}
||||||||
| 最近运行目录 | +{{exeRoot}} | +