feat: sync httpdns sdk/platform updates without large binaries
This commit is contained in:
@@ -28,19 +28,20 @@ func (this *SdkCheckAction) RunGet(params struct {
|
||||
return
|
||||
}
|
||||
|
||||
version := strings.TrimSpace(params.Version)
|
||||
t := strings.ToLower(strings.TrimSpace(params.Type))
|
||||
if t == "doc" {
|
||||
docPath := findUploadedSDKDocPath(platform, params.Version)
|
||||
docPath := findUploadedSDKDocPath(platform, version)
|
||||
if len(docPath) == 0 {
|
||||
this.Data["exists"] = false
|
||||
this.Data["message"] = "Documentation is unavailable, please upload first"
|
||||
this.Data["message"] = "当前平台/版本尚未上传集成文档"
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
downloadURL := "/httpdns/apps/sdk/doc?platform=" + url.QueryEscape(platform)
|
||||
if len(strings.TrimSpace(params.Version)) > 0 {
|
||||
downloadURL += "&version=" + url.QueryEscape(strings.TrimSpace(params.Version))
|
||||
if len(version) > 0 {
|
||||
downloadURL += "&version=" + url.QueryEscape(version)
|
||||
}
|
||||
this.Data["exists"] = true
|
||||
this.Data["url"] = downloadURL
|
||||
@@ -48,17 +49,17 @@ func (this *SdkCheckAction) RunGet(params struct {
|
||||
return
|
||||
}
|
||||
|
||||
archivePath := findSDKArchivePath(filename, params.Version)
|
||||
archivePath := findSDKArchivePath(filename, version)
|
||||
if len(archivePath) == 0 {
|
||||
this.Data["exists"] = false
|
||||
this.Data["message"] = "SDK package is unavailable, please upload first"
|
||||
this.Data["message"] = "当前平台/版本尚未上传 SDK 安装包"
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
downloadURL := "/httpdns/apps/sdk/download?platform=" + url.QueryEscape(platform)
|
||||
if len(strings.TrimSpace(params.Version)) > 0 {
|
||||
downloadURL += "&version=" + url.QueryEscape(strings.TrimSpace(params.Version))
|
||||
if len(version) > 0 {
|
||||
downloadURL += "&version=" + url.QueryEscape(version)
|
||||
}
|
||||
downloadURL += "&raw=1"
|
||||
this.Data["exists"] = true
|
||||
|
||||
@@ -3,7 +3,6 @@ package apps
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
)
|
||||
@@ -20,7 +19,7 @@ func (this *SdkDocAction) RunGet(params struct {
|
||||
Platform string
|
||||
Version string
|
||||
}) {
|
||||
platform, _, readmeRelativePath, _, err := resolveSDKPlatform(params.Platform)
|
||||
platform, _, _, _, err := resolveSDKPlatform(params.Platform)
|
||||
if err != nil {
|
||||
this.Data["exists"] = false
|
||||
this.Data["message"] = err.Error()
|
||||
@@ -28,35 +27,25 @@ func (this *SdkDocAction) RunGet(params struct {
|
||||
return
|
||||
}
|
||||
|
||||
var data []byte
|
||||
uploadedDocPath := findUploadedSDKDocPath(platform, params.Version)
|
||||
if len(uploadedDocPath) > 0 {
|
||||
data, err = os.ReadFile(uploadedDocPath)
|
||||
}
|
||||
|
||||
sdkRoot, sdkRootErr := findSDKRoot()
|
||||
if len(data) == 0 && sdkRootErr == nil {
|
||||
readmePath := filepath.Join(sdkRoot, readmeRelativePath)
|
||||
data, err = os.ReadFile(readmePath)
|
||||
}
|
||||
|
||||
if len(data) == 0 {
|
||||
localDocPath := findLocalSDKDocPath(platform)
|
||||
if len(localDocPath) > 0 {
|
||||
data, err = os.ReadFile(localDocPath)
|
||||
}
|
||||
}
|
||||
|
||||
if len(data) == 0 || err != nil {
|
||||
docPath := findUploadedSDKDocPath(platform, params.Version)
|
||||
if len(docPath) == 0 {
|
||||
this.Data["exists"] = false
|
||||
this.Data["message"] = "SDK documentation is not found on server, please upload first"
|
||||
this.Data["message"] = "当前平台/版本尚未上传集成文档"
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
downloadName := filepath.Base(uploadedDocPath)
|
||||
data, err := os.ReadFile(docPath)
|
||||
if err != nil || len(data) == 0 {
|
||||
this.Data["exists"] = false
|
||||
this.Data["message"] = "读取集成文档失败"
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
downloadName := filepath.Base(docPath)
|
||||
if len(downloadName) == 0 || downloadName == "." || downloadName == string(filepath.Separator) {
|
||||
downloadName = "httpdns-sdk-" + strings.ToLower(platform) + ".md"
|
||||
downloadName = "sdk-doc.md"
|
||||
}
|
||||
|
||||
this.AddHeader("Content-Type", "text/markdown; charset=utf-8")
|
||||
|
||||
@@ -32,7 +32,7 @@ func (this *SdkDownloadAction) RunGet(params struct {
|
||||
archivePath := findSDKArchivePath(filename, params.Version)
|
||||
if len(archivePath) == 0 {
|
||||
this.Data["exists"] = false
|
||||
this.Data["message"] = "SDK archive not found on server, please upload first: " + filename
|
||||
this.Data["message"] = "当前平台/版本尚未上传 SDK 安装包"
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
@@ -40,7 +40,7 @@ func (this *SdkDownloadAction) RunGet(params struct {
|
||||
fp, err := os.Open(archivePath)
|
||||
if err != nil {
|
||||
this.Data["exists"] = false
|
||||
this.Data["message"] = "failed to open SDK archive: " + err.Error()
|
||||
this.Data["message"] = "打开 SDK 安装包失败: " + err.Error()
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,16 +1,183 @@
|
||||
package apps
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
)
|
||||
|
||||
type sdkUploadMeta struct {
|
||||
Platform string `json:"platform"`
|
||||
Version string `json:"version"`
|
||||
FileType string `json:"fileType"` // sdk | doc
|
||||
Filename string `json:"filename"`
|
||||
UpdatedAt int64 `json:"updatedAt"`
|
||||
}
|
||||
|
||||
type sdkUploadMetaRecord struct {
|
||||
Meta sdkUploadMeta
|
||||
Dir string
|
||||
FilePath string
|
||||
}
|
||||
|
||||
func sdkUploadMetaFilename(platform string, version string, fileType string) string {
|
||||
platform = strings.ToLower(strings.TrimSpace(platform))
|
||||
version = strings.TrimSpace(version)
|
||||
fileType = strings.ToLower(strings.TrimSpace(fileType))
|
||||
return ".httpdns-sdk-meta-" + platform + "-v" + version + "-" + fileType + ".json"
|
||||
}
|
||||
|
||||
func isSDKUploadMetaFile(name string) bool {
|
||||
name = strings.ToLower(strings.TrimSpace(name))
|
||||
return strings.HasPrefix(name, ".httpdns-sdk-meta-") && strings.HasSuffix(name, ".json")
|
||||
}
|
||||
|
||||
func parseSDKPlatformFromDownloadFilename(downloadFilename string) string {
|
||||
name := strings.ToLower(strings.TrimSpace(downloadFilename))
|
||||
if !strings.HasPrefix(name, "httpdns-sdk-") || !strings.HasSuffix(name, ".zip") {
|
||||
return ""
|
||||
}
|
||||
|
||||
platform := strings.TrimSuffix(strings.TrimPrefix(name, "httpdns-sdk-"), ".zip")
|
||||
switch platform {
|
||||
case "android", "ios", "flutter":
|
||||
return platform
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func listSDKUploadMetaRecords() []sdkUploadMetaRecord {
|
||||
type wrapped struct {
|
||||
record sdkUploadMetaRecord
|
||||
modTime time.Time
|
||||
}
|
||||
|
||||
byKey := map[string]wrapped{}
|
||||
for _, dir := range sdkUploadSearchDirs() {
|
||||
entries, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
continue
|
||||
}
|
||||
|
||||
name := entry.Name()
|
||||
if !isSDKUploadMetaFile(name) {
|
||||
continue
|
||||
}
|
||||
|
||||
metaPath := filepath.Join(dir, name)
|
||||
data, err := os.ReadFile(metaPath)
|
||||
if err != nil || len(data) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
var meta sdkUploadMeta
|
||||
if err = json.Unmarshal(data, &meta); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
meta.Platform = strings.ToLower(strings.TrimSpace(meta.Platform))
|
||||
meta.Version = strings.TrimSpace(meta.Version)
|
||||
meta.FileType = strings.ToLower(strings.TrimSpace(meta.FileType))
|
||||
meta.Filename = filepath.Base(strings.TrimSpace(meta.Filename))
|
||||
if len(meta.Platform) == 0 || len(meta.Version) == 0 || len(meta.Filename) == 0 {
|
||||
continue
|
||||
}
|
||||
if meta.FileType != "sdk" && meta.FileType != "doc" {
|
||||
continue
|
||||
}
|
||||
if strings.Contains(meta.Filename, "..") || strings.Contains(meta.Filename, "/") || strings.Contains(meta.Filename, "\\") {
|
||||
continue
|
||||
}
|
||||
|
||||
filePath := filepath.Join(dir, meta.Filename)
|
||||
fileStat, err := os.Stat(filePath)
|
||||
if err != nil || fileStat.IsDir() || fileStat.Size() <= 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
metaStat, err := os.Stat(metaPath)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if meta.UpdatedAt <= 0 {
|
||||
meta.UpdatedAt = metaStat.ModTime().Unix()
|
||||
}
|
||||
|
||||
key := meta.Platform + "|" + meta.Version + "|" + meta.FileType
|
||||
current := wrapped{
|
||||
record: sdkUploadMetaRecord{
|
||||
Meta: meta,
|
||||
Dir: dir,
|
||||
FilePath: filePath,
|
||||
},
|
||||
modTime: metaStat.ModTime(),
|
||||
}
|
||||
old, ok := byKey[key]
|
||||
if !ok ||
|
||||
current.record.Meta.UpdatedAt > old.record.Meta.UpdatedAt ||
|
||||
(current.record.Meta.UpdatedAt == old.record.Meta.UpdatedAt && current.modTime.After(old.modTime)) ||
|
||||
(current.record.Meta.UpdatedAt == old.record.Meta.UpdatedAt && current.modTime.Equal(old.modTime) && current.record.FilePath > old.record.FilePath) {
|
||||
byKey[key] = current
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result := make([]sdkUploadMetaRecord, 0, len(byKey))
|
||||
for _, item := range byKey {
|
||||
result = append(result, item.record)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func findSDKUploadFileByMeta(platform string, version string, fileType string) string {
|
||||
platform = strings.ToLower(strings.TrimSpace(platform))
|
||||
version = strings.TrimSpace(version)
|
||||
fileType = strings.ToLower(strings.TrimSpace(fileType))
|
||||
if len(platform) == 0 || len(version) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
for _, record := range listSDKUploadMetaRecords() {
|
||||
if record.Meta.Platform == platform && record.Meta.Version == version && record.Meta.FileType == fileType {
|
||||
return record.FilePath
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func findNewestSDKUploadFileByMeta(platform string, fileType string) string {
|
||||
platform = strings.ToLower(strings.TrimSpace(platform))
|
||||
fileType = strings.ToLower(strings.TrimSpace(fileType))
|
||||
if len(platform) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
var foundPath string
|
||||
var foundUpdatedAt int64
|
||||
for _, record := range listSDKUploadMetaRecords() {
|
||||
if record.Meta.Platform != platform || record.Meta.FileType != fileType {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(foundPath) == 0 || record.Meta.UpdatedAt > foundUpdatedAt || (record.Meta.UpdatedAt == foundUpdatedAt && record.FilePath > foundPath) {
|
||||
foundPath = record.FilePath
|
||||
foundUpdatedAt = record.Meta.UpdatedAt
|
||||
}
|
||||
}
|
||||
return foundPath
|
||||
}
|
||||
|
||||
func sdkUploadDir() string {
|
||||
dirs := sdkUploadDirs()
|
||||
if len(dirs) > 0 {
|
||||
@@ -27,6 +194,7 @@ func sdkUploadDirs() []string {
|
||||
filepath.Clean(Tea.Root + "/../edge-user/data/httpdns/sdk"),
|
||||
filepath.Clean(Tea.Root + "/../../data/httpdns/sdk"),
|
||||
}
|
||||
|
||||
results := make([]string, 0, len(candidates))
|
||||
seen := map[string]bool{}
|
||||
for _, dir := range candidates {
|
||||
@@ -54,67 +222,6 @@ func sdkUploadSearchDirs() []string {
|
||||
return results
|
||||
}
|
||||
|
||||
func findFirstExistingDir(paths []string) string {
|
||||
for _, path := range paths {
|
||||
stat, err := os.Stat(path)
|
||||
if err == nil && stat.IsDir() {
|
||||
return path
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func findFirstExistingFile(paths []string) string {
|
||||
for _, path := range paths {
|
||||
stat, err := os.Stat(path)
|
||||
if err == nil && !stat.IsDir() && stat.Size() > 0 {
|
||||
return path
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func findNewestExistingFile(paths []string) string {
|
||||
type fileInfo struct {
|
||||
path string
|
||||
modTime time.Time
|
||||
}
|
||||
result := fileInfo{}
|
||||
for _, path := range paths {
|
||||
stat, err := os.Stat(path)
|
||||
if err != nil || stat.IsDir() {
|
||||
continue
|
||||
}
|
||||
if stat.Size() <= 0 {
|
||||
continue
|
||||
}
|
||||
if len(result.path) == 0 || stat.ModTime().After(result.modTime) || (stat.ModTime().Equal(result.modTime) && path > result.path) {
|
||||
result.path = path
|
||||
result.modTime = stat.ModTime()
|
||||
}
|
||||
}
|
||||
return result.path
|
||||
}
|
||||
|
||||
func findSDKRoot() (string, error) {
|
||||
candidates := []string{
|
||||
filepath.Clean(Tea.Root + "/EdgeHttpDNS/sdk"),
|
||||
filepath.Clean(Tea.Root + "/edge-httpdns/sdk"),
|
||||
filepath.Clean(Tea.Root + "/edge-httpdns/edge-httpdns/sdk"),
|
||||
filepath.Clean(Tea.Root + "/../EdgeHttpDNS/sdk"),
|
||||
filepath.Clean(Tea.Root + "/../../EdgeHttpDNS/sdk"),
|
||||
filepath.Clean(Tea.Root + "/../edge-httpdns/sdk"),
|
||||
filepath.Clean(Tea.Root + "/../../edge-httpdns/sdk"),
|
||||
}
|
||||
|
||||
dir := findFirstExistingDir(candidates)
|
||||
if len(dir) > 0 {
|
||||
return dir, nil
|
||||
}
|
||||
|
||||
return "", errors.New("SDK files are not found on current server")
|
||||
}
|
||||
|
||||
func resolveSDKPlatform(platform string) (key string, relativeDir string, readmeRelativePath string, downloadFilename string, err error) {
|
||||
switch strings.ToLower(strings.TrimSpace(platform)) {
|
||||
case "android":
|
||||
@@ -122,52 +229,23 @@ func resolveSDKPlatform(platform string) (key string, relativeDir string, readme
|
||||
case "ios":
|
||||
return "ios", "ios", "ios/README.md", "httpdns-sdk-ios.zip", nil
|
||||
case "flutter":
|
||||
return "flutter", "flutter/aliyun_httpdns", "flutter/aliyun_httpdns/README.md", "httpdns-sdk-flutter.zip", nil
|
||||
return "flutter", "flutter/new_httpdns", "flutter/new_httpdns/README.md", "httpdns-sdk-flutter.zip", nil
|
||||
default:
|
||||
return "", "", "", "", errors.New("invalid platform, expected one of: android, ios, flutter")
|
||||
return "", "", "", "", errors.New("不支持的平台,可选值:android、ios、flutter")
|
||||
}
|
||||
}
|
||||
|
||||
func findSDKArchivePath(downloadFilename string, version string) string {
|
||||
searchDirs := sdkUploadSearchDirs()
|
||||
|
||||
normalizedVersion := strings.TrimSpace(version)
|
||||
base := strings.TrimSuffix(downloadFilename, ".zip")
|
||||
if len(normalizedVersion) > 0 {
|
||||
versionFiles := []string{}
|
||||
for _, dir := range searchDirs {
|
||||
versionFiles = append(versionFiles, filepath.Join(dir, base+"-v"+normalizedVersion+".zip"))
|
||||
}
|
||||
if path := findFirstExistingFile(versionFiles); len(path) > 0 {
|
||||
return path
|
||||
}
|
||||
platform := parseSDKPlatformFromDownloadFilename(downloadFilename)
|
||||
if len(platform) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
patternName := base + "-v*.zip"
|
||||
matches := []string{}
|
||||
for _, dir := range searchDirs {
|
||||
found, _ := filepath.Glob(filepath.Join(dir, patternName))
|
||||
for _, file := range found {
|
||||
stat, err := os.Stat(file)
|
||||
if err == nil && !stat.IsDir() {
|
||||
matches = append(matches, file)
|
||||
}
|
||||
}
|
||||
normalizedVersion := strings.TrimSpace(version)
|
||||
if len(normalizedVersion) > 0 {
|
||||
return findSDKUploadFileByMeta(platform, normalizedVersion, "sdk")
|
||||
}
|
||||
if len(matches) > 0 {
|
||||
return findNewestExistingFile(matches)
|
||||
}
|
||||
|
||||
exactFiles := []string{}
|
||||
for _, dir := range searchDirs {
|
||||
exactFiles = append(exactFiles, filepath.Join(dir, downloadFilename))
|
||||
}
|
||||
if path := findFirstExistingFile(exactFiles); len(path) > 0 {
|
||||
return path
|
||||
}
|
||||
|
||||
return ""
|
||||
return findNewestSDKUploadFileByMeta(platform, "sdk")
|
||||
}
|
||||
|
||||
func findUploadedSDKDocPath(platform string, version string) string {
|
||||
@@ -176,42 +254,9 @@ func findUploadedSDKDocPath(platform string, version string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
searchDirs := sdkUploadSearchDirs()
|
||||
normalizedVersion := strings.TrimSpace(version)
|
||||
if len(normalizedVersion) > 0 {
|
||||
exactVersion := []string{}
|
||||
for _, dir := range searchDirs {
|
||||
exactVersion = append(exactVersion, filepath.Join(dir, "httpdns-sdk-"+platform+"-v"+normalizedVersion+".md"))
|
||||
}
|
||||
if file := findFirstExistingFile(exactVersion); len(file) > 0 {
|
||||
return file
|
||||
}
|
||||
return ""
|
||||
return findSDKUploadFileByMeta(platform, normalizedVersion, "doc")
|
||||
}
|
||||
|
||||
matches := []string{}
|
||||
for _, dir := range searchDirs {
|
||||
pattern := filepath.Join(dir, "httpdns-sdk-"+platform+"-v*.md")
|
||||
found, _ := filepath.Glob(pattern)
|
||||
matches = append(matches, found...)
|
||||
}
|
||||
if len(matches) > 0 {
|
||||
sort.Strings(matches)
|
||||
return findNewestExistingFile(matches)
|
||||
}
|
||||
|
||||
exact := []string{}
|
||||
for _, dir := range searchDirs {
|
||||
exact = append(exact, filepath.Join(dir, "httpdns-sdk-"+platform+".md"))
|
||||
}
|
||||
return findFirstExistingFile(exact)
|
||||
}
|
||||
|
||||
func findLocalSDKDocPath(platform string) string {
|
||||
filename := strings.ToLower(strings.TrimSpace(platform)) + ".md"
|
||||
candidates := []string{
|
||||
filepath.Clean(Tea.Root + "/edge-admin/web/views/@default/httpdns/apps/docs/" + filename),
|
||||
filepath.Clean(Tea.Root + "/EdgeAdmin/web/views/@default/httpdns/apps/docs/" + filename),
|
||||
}
|
||||
return findFirstExistingFile(candidates)
|
||||
return findNewestSDKUploadFileByMeta(platform, "doc")
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package apps
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -14,6 +15,8 @@ import (
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
)
|
||||
|
||||
const sdkUploadMaxFileSize = 20 * 1024 * 1024 // 20MB
|
||||
|
||||
type SdkUploadAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
@@ -52,7 +55,7 @@ func (this *SdkUploadAction) RunPost(params struct {
|
||||
}) {
|
||||
params.Must.Field("appId", params.AppId).Gt(0, "请选择应用")
|
||||
|
||||
platform, _, _, downloadFilename, err := resolveSDKPlatform(params.Platform)
|
||||
platform, _, _, _, err := resolveSDKPlatform(params.Platform)
|
||||
if err != nil {
|
||||
this.Fail(err.Error())
|
||||
return
|
||||
@@ -70,57 +73,21 @@ func (this *SdkUploadAction) RunPost(params struct {
|
||||
}
|
||||
|
||||
uploadDir := sdkUploadDir()
|
||||
err = os.MkdirAll(uploadDir, 0755)
|
||||
if err != nil {
|
||||
if err = os.MkdirAll(uploadDir, 0755); err != nil {
|
||||
this.Fail("创建上传目录失败: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if params.SdkFile != nil {
|
||||
filename := strings.ToLower(strings.TrimSpace(params.SdkFile.Filename))
|
||||
if !strings.HasSuffix(filename, ".zip") {
|
||||
this.Fail("SDK 包仅支持 .zip 文件")
|
||||
return
|
||||
}
|
||||
|
||||
sdkData, readErr := params.SdkFile.Read()
|
||||
if readErr != nil {
|
||||
this.Fail("读取 SDK 包失败: " + readErr.Error())
|
||||
return
|
||||
}
|
||||
if len(sdkData) == 0 {
|
||||
this.Fail("SDK 包文件为空,请重新上传")
|
||||
return
|
||||
}
|
||||
|
||||
baseName := strings.TrimSuffix(downloadFilename, ".zip")
|
||||
err = saveSDKUploadFile(uploadDir, baseName+"-v"+version+".zip", sdkData)
|
||||
if err != nil {
|
||||
this.Fail("保存 SDK 包失败: " + err.Error())
|
||||
if err = this.saveUploadedItem(uploadDir, platform, version, "sdk", params.SdkFile); err != nil {
|
||||
this.Fail(err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if params.DocFile != nil {
|
||||
docName := strings.ToLower(strings.TrimSpace(params.DocFile.Filename))
|
||||
if !strings.HasSuffix(docName, ".md") {
|
||||
this.Fail("集成文档仅支持 .md 文件")
|
||||
return
|
||||
}
|
||||
|
||||
docData, readErr := params.DocFile.Read()
|
||||
if readErr != nil {
|
||||
this.Fail("读取集成文档失败: " + readErr.Error())
|
||||
return
|
||||
}
|
||||
if len(docData) == 0 {
|
||||
this.Fail("集成文档文件为空,请重新上传")
|
||||
return
|
||||
}
|
||||
|
||||
err = saveSDKUploadFile(uploadDir, "httpdns-sdk-"+platform+"-v"+version+".md", docData)
|
||||
if err != nil {
|
||||
this.Fail("保存集成文档失败: " + err.Error())
|
||||
if err = this.saveUploadedItem(uploadDir, platform, version, "doc", params.DocFile); err != nil {
|
||||
this.Fail(err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -128,6 +95,52 @@ func (this *SdkUploadAction) RunPost(params struct {
|
||||
this.Success()
|
||||
}
|
||||
|
||||
func (this *SdkUploadAction) saveUploadedItem(uploadDir string, platform string, version string, fileType string, file *actions.File) error {
|
||||
expectedExt := ".md"
|
||||
displayType := "集成文档"
|
||||
if fileType == "sdk" {
|
||||
expectedExt = ".zip"
|
||||
displayType = "SDK 包"
|
||||
}
|
||||
|
||||
filename, err := normalizeUploadedFilename(file.Filename, expectedExt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if file.Size > sdkUploadMaxFileSize {
|
||||
return errors.New(displayType + "文件不能超过 20MB")
|
||||
}
|
||||
|
||||
data, err := file.Read()
|
||||
if err != nil {
|
||||
return errors.New("读取" + displayType + "失败: " + err.Error())
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return errors.New(displayType + "文件为空,请重新上传")
|
||||
}
|
||||
if len(data) > sdkUploadMaxFileSize {
|
||||
return errors.New(displayType + "文件不能超过 20MB")
|
||||
}
|
||||
|
||||
if err = saveSDKUploadFile(uploadDir, filename, data); err != nil {
|
||||
return errors.New("保存" + displayType + "失败: " + err.Error())
|
||||
}
|
||||
|
||||
err = saveSDKUploadMetaRecord(uploadDir, sdkUploadMeta{
|
||||
Platform: platform,
|
||||
Version: version,
|
||||
FileType: fileType,
|
||||
Filename: filename,
|
||||
UpdatedAt: time.Now().Unix(),
|
||||
})
|
||||
if err != nil {
|
||||
return errors.New("保存上传元信息失败: " + err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func normalizeSDKVersion(version string) (string, error) {
|
||||
version = strings.TrimSpace(version)
|
||||
if len(version) == 0 {
|
||||
@@ -142,6 +155,26 @@ func normalizeSDKVersion(version string) (string, error) {
|
||||
return version, nil
|
||||
}
|
||||
|
||||
func normalizeUploadedFilename(raw string, expectedExt string) (string, error) {
|
||||
filename := filepath.Base(strings.TrimSpace(raw))
|
||||
if len(filename) == 0 || filename == "." || filename == string(filepath.Separator) {
|
||||
return "", errors.New("文件名不能为空")
|
||||
}
|
||||
if strings.Contains(filename, "/") || strings.Contains(filename, "\\") || strings.Contains(filename, "..") {
|
||||
return "", errors.New("文件名不合法")
|
||||
}
|
||||
|
||||
actualExt := strings.ToLower(filepath.Ext(filename))
|
||||
if actualExt != strings.ToLower(expectedExt) {
|
||||
if expectedExt == ".zip" {
|
||||
return "", errors.New("SDK 包仅支持 .zip 文件")
|
||||
}
|
||||
return "", errors.New("集成文档仅支持 .md 文件")
|
||||
}
|
||||
|
||||
return filename, nil
|
||||
}
|
||||
|
||||
func saveSDKUploadFile(baseDir string, filename string, data []byte) error {
|
||||
targetPath := filepath.Join(baseDir, filename)
|
||||
tmpPath := targetPath + ".tmp"
|
||||
@@ -152,6 +185,35 @@ func saveSDKUploadFile(baseDir string, filename string, data []byte) error {
|
||||
return os.Rename(tmpPath, targetPath)
|
||||
}
|
||||
|
||||
func saveSDKUploadMetaRecord(baseDir string, meta sdkUploadMeta) error {
|
||||
meta.Platform = strings.ToLower(strings.TrimSpace(meta.Platform))
|
||||
meta.Version = strings.TrimSpace(meta.Version)
|
||||
meta.FileType = strings.ToLower(strings.TrimSpace(meta.FileType))
|
||||
meta.Filename = filepath.Base(strings.TrimSpace(meta.Filename))
|
||||
if len(meta.Platform) == 0 || len(meta.Version) == 0 || len(meta.FileType) == 0 || len(meta.Filename) == 0 {
|
||||
return errors.New("上传元信息不完整")
|
||||
}
|
||||
|
||||
metaFilename := sdkUploadMetaFilename(meta.Platform, meta.Version, meta.FileType)
|
||||
metaPath := filepath.Join(baseDir, metaFilename)
|
||||
if data, err := os.ReadFile(metaPath); err == nil && len(data) > 0 {
|
||||
var oldMeta sdkUploadMeta
|
||||
if json.Unmarshal(data, &oldMeta) == nil {
|
||||
oldFile := filepath.Base(strings.TrimSpace(oldMeta.Filename))
|
||||
if len(oldFile) > 0 && oldFile != meta.Filename {
|
||||
_ = os.Remove(filepath.Join(baseDir, oldFile))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data, err := json.Marshal(meta)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return saveSDKUploadFile(baseDir, metaFilename, data)
|
||||
}
|
||||
|
||||
func listUploadedSDKFiles() []map[string]interface{} {
|
||||
type item struct {
|
||||
Name string
|
||||
@@ -162,45 +224,26 @@ func listUploadedSDKFiles() []map[string]interface{} {
|
||||
UpdatedAt int64
|
||||
}
|
||||
|
||||
byName := map[string]item{}
|
||||
for _, dir := range sdkUploadSearchDirs() {
|
||||
entries, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
items := make([]item, 0)
|
||||
for _, record := range listSDKUploadMetaRecords() {
|
||||
stat, err := os.Stat(record.FilePath)
|
||||
if err != nil || stat.IsDir() {
|
||||
continue
|
||||
}
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
continue
|
||||
}
|
||||
name := entry.Name()
|
||||
platform, version, fileType, ok := parseSDKUploadFilename(name)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
info, statErr := entry.Info()
|
||||
if statErr != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
current := item{
|
||||
Name: name,
|
||||
Platform: platform,
|
||||
FileType: fileType,
|
||||
Version: version,
|
||||
SizeBytes: info.Size(),
|
||||
UpdatedAt: info.ModTime().Unix(),
|
||||
}
|
||||
old, exists := byName[name]
|
||||
if !exists || current.UpdatedAt >= old.UpdatedAt {
|
||||
byName[name] = current
|
||||
}
|
||||
fileType := "SDK 包"
|
||||
if record.Meta.FileType == "doc" {
|
||||
fileType = "集成文档"
|
||||
}
|
||||
}
|
||||
|
||||
items := make([]item, 0, len(byName))
|
||||
for _, it := range byName {
|
||||
items = append(items, it)
|
||||
items = append(items, item{
|
||||
Name: filepath.Base(record.FilePath),
|
||||
Platform: record.Meta.Platform,
|
||||
FileType: fileType,
|
||||
Version: record.Meta.Version,
|
||||
SizeBytes: stat.Size(),
|
||||
UpdatedAt: stat.ModTime().Unix(),
|
||||
})
|
||||
}
|
||||
|
||||
sort.Slice(items, func(i, j int) bool {
|
||||
@@ -224,55 +267,21 @@ func listUploadedSDKFiles() []map[string]interface{} {
|
||||
return result
|
||||
}
|
||||
|
||||
func parseSDKUploadFilename(filename string) (platform string, version string, fileType string, ok bool) {
|
||||
if !strings.HasPrefix(filename, "httpdns-sdk-") {
|
||||
return "", "", "", false
|
||||
}
|
||||
|
||||
ext := ""
|
||||
switch {
|
||||
case strings.HasSuffix(filename, ".zip"):
|
||||
ext = ".zip"
|
||||
fileType = "SDK包"
|
||||
case strings.HasSuffix(filename, ".md"):
|
||||
ext = ".md"
|
||||
fileType = "集成文档"
|
||||
default:
|
||||
return "", "", "", false
|
||||
}
|
||||
|
||||
main := strings.TrimSuffix(strings.TrimPrefix(filename, "httpdns-sdk-"), ext)
|
||||
version = ""
|
||||
if idx := strings.Index(main, "-v"); idx > 0 && idx+2 < len(main) {
|
||||
version = main[idx+2:]
|
||||
main = main[:idx]
|
||||
}
|
||||
|
||||
main = strings.ToLower(strings.TrimSpace(main))
|
||||
switch main {
|
||||
case "android", "ios", "flutter":
|
||||
platform = main
|
||||
if len(version) == 0 {
|
||||
version = "-"
|
||||
}
|
||||
return platform, version, fileType, true
|
||||
default:
|
||||
return "", "", "", false
|
||||
}
|
||||
}
|
||||
|
||||
func formatSDKFileSize(size int64) string {
|
||||
if size < 1024 {
|
||||
return strconv.FormatInt(size, 10) + " B"
|
||||
}
|
||||
|
||||
sizeKB := float64(size) / 1024
|
||||
if sizeKB < 1024 {
|
||||
return strconv.FormatFloat(sizeKB, 'f', 1, 64) + " KB"
|
||||
}
|
||||
|
||||
sizeMB := sizeKB / 1024
|
||||
if sizeMB < 1024 {
|
||||
return strconv.FormatFloat(sizeMB, 'f', 1, 64) + " MB"
|
||||
}
|
||||
|
||||
sizeGB := sizeMB / 1024
|
||||
return strconv.FormatFloat(sizeGB, 'f', 1, 64) + " GB"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package apps
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -34,19 +35,16 @@ func (this *SdkUploadDeleteAction) RunPost(params struct {
|
||||
this.Fail("文件名不合法")
|
||||
return
|
||||
}
|
||||
if !strings.HasPrefix(filename, "httpdns-sdk-") {
|
||||
this.Fail("不允许删除该文件")
|
||||
return
|
||||
}
|
||||
if !(strings.HasSuffix(filename, ".zip") || strings.HasSuffix(filename, ".md")) {
|
||||
this.Fail("不允许删除该文件")
|
||||
lowName := strings.ToLower(filename)
|
||||
if !strings.HasSuffix(lowName, ".zip") && !strings.HasSuffix(lowName, ".md") {
|
||||
this.Fail("仅允许删除 .zip 或 .md 文件")
|
||||
return
|
||||
}
|
||||
|
||||
for _, dir := range sdkUploadDirs() {
|
||||
fullPath := filepath.Join(dir, filename)
|
||||
_, err := os.Stat(fullPath)
|
||||
if err != nil {
|
||||
stat, err := os.Stat(fullPath)
|
||||
if err != nil || stat.IsDir() {
|
||||
continue
|
||||
}
|
||||
if err = os.Remove(fullPath); err != nil {
|
||||
@@ -55,5 +53,38 @@ func (this *SdkUploadDeleteAction) RunPost(params struct {
|
||||
}
|
||||
}
|
||||
|
||||
// 删除引用该文件的元数据
|
||||
for _, dir := range sdkUploadDirs() {
|
||||
entries, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
continue
|
||||
}
|
||||
if !isSDKUploadMetaFile(entry.Name()) {
|
||||
continue
|
||||
}
|
||||
|
||||
metaPath := filepath.Join(dir, entry.Name())
|
||||
data, err := os.ReadFile(metaPath)
|
||||
if err != nil || len(data) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
var meta sdkUploadMeta
|
||||
if err = json.Unmarshal(data, &meta); err != nil {
|
||||
continue
|
||||
}
|
||||
if filepath.Base(strings.TrimSpace(meta.Filename)) != filename {
|
||||
continue
|
||||
}
|
||||
|
||||
_ = os.Remove(metaPath)
|
||||
}
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
)
|
||||
|
||||
@@ -50,7 +52,22 @@ func (this *IndexAction) RunGet(params struct {
|
||||
this.Data["nodeDatetime"] = nodeDatetime
|
||||
this.Data["nodeTimeDiff"] = nodeTimeDiff
|
||||
|
||||
this.Data["shouldUpgrade"] = false
|
||||
this.Data["newVersion"] = ""
|
||||
osName := strings.TrimSpace(status.GetString("os"))
|
||||
if len(osName) > 0 {
|
||||
checkVersionResp, err := this.RPC().HTTPDNSNodeRPC().CheckHTTPDNSNodeLatestVersion(this.AdminContext(), &pb.CheckHTTPDNSNodeLatestVersionRequest{
|
||||
Os: osName,
|
||||
Arch: strings.TrimSpace(status.GetString("arch")),
|
||||
CurrentVersion: strings.TrimSpace(status.GetString("buildVersion")),
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.Data["shouldUpgrade"] = checkVersionResp.GetHasNewVersion()
|
||||
this.Data["newVersion"] = checkVersionResp.GetNewVersion()
|
||||
} else {
|
||||
this.Data["shouldUpgrade"] = false
|
||||
this.Data["newVersion"] = ""
|
||||
}
|
||||
this.Show()
|
||||
}
|
||||
|
||||
@@ -22,14 +22,14 @@ func findHTTPDNSClusterMap(parent *actionutils.ParentAction, clusterID int64) (m
|
||||
return maps.Map{
|
||||
"id": clusterID,
|
||||
"name": "",
|
||||
"installDir": "/opt/edge-httpdns",
|
||||
"installDir": "/root/edge-httpdns",
|
||||
}, nil
|
||||
}
|
||||
|
||||
cluster := resp.GetCluster()
|
||||
installDir := strings.TrimSpace(cluster.GetInstallDir())
|
||||
if len(installDir) == 0 {
|
||||
installDir = "/opt/edge-httpdns"
|
||||
installDir = "/root/edge-httpdns"
|
||||
}
|
||||
return maps.Map{
|
||||
"id": cluster.GetId(),
|
||||
@@ -93,7 +93,7 @@ func findHTTPDNSNodeMap(parent *actionutils.ParentAction, nodeID int64) (maps.Ma
|
||||
|
||||
installDir := strings.TrimSpace(node.GetInstallDir())
|
||||
if len(installDir) == 0 {
|
||||
installDir = "/opt/edge-httpdns"
|
||||
installDir = "/root/edge-httpdns"
|
||||
}
|
||||
|
||||
clusterMap, err := findHTTPDNSClusterMap(parent, node.GetClusterId())
|
||||
@@ -137,22 +137,22 @@ func findHTTPDNSNodeMap(parent *actionutils.ParentAction, nodeID int64) (maps.Ma
|
||||
}
|
||||
|
||||
return maps.Map{
|
||||
"id": node.GetId(),
|
||||
"clusterId": node.GetClusterId(),
|
||||
"name": node.GetName(),
|
||||
"isOn": node.GetIsOn(),
|
||||
"isUp": node.GetIsUp(),
|
||||
"isInstalled": node.GetIsInstalled(),
|
||||
"isActive": node.GetIsActive(),
|
||||
"uniqueId": node.GetUniqueId(),
|
||||
"secret": node.GetSecret(),
|
||||
"installDir": installDir,
|
||||
"status": statusMap,
|
||||
"id": node.GetId(),
|
||||
"clusterId": node.GetClusterId(),
|
||||
"name": node.GetName(),
|
||||
"isOn": node.GetIsOn(),
|
||||
"isUp": node.GetIsUp(),
|
||||
"isInstalled": node.GetIsInstalled(),
|
||||
"isActive": node.GetIsActive(),
|
||||
"uniqueId": node.GetUniqueId(),
|
||||
"secret": node.GetSecret(),
|
||||
"installDir": installDir,
|
||||
"status": statusMap,
|
||||
"installStatus": installStatusMap,
|
||||
"cluster": clusterMap,
|
||||
"login": loginMap,
|
||||
"apiNodeAddrs": []string{},
|
||||
"ipAddresses": ipAddresses,
|
||||
"cluster": clusterMap,
|
||||
"login": loginMap,
|
||||
"apiNodeAddrs": []string{},
|
||||
"ipAddresses": ipAddresses,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -165,21 +165,23 @@ func decodeNodeStatus(raw []byte) maps.Map {
|
||||
memText := fmt.Sprintf("%.2f%%", status.MemoryUsage*100)
|
||||
|
||||
return maps.Map{
|
||||
"isActive": status.IsActive,
|
||||
"updatedAt": status.UpdatedAt,
|
||||
"hostname": status.Hostname,
|
||||
"hostIP": status.HostIP,
|
||||
"cpuUsage": status.CPUUsage,
|
||||
"cpuUsageText": cpuText,
|
||||
"memUsage": status.MemoryUsage,
|
||||
"memUsageText": memText,
|
||||
"load1m": status.Load1m,
|
||||
"load5m": status.Load5m,
|
||||
"load15m": status.Load15m,
|
||||
"buildVersion": status.BuildVersion,
|
||||
"cpuPhysicalCount": status.CPUPhysicalCount,
|
||||
"cpuLogicalCount": status.CPULogicalCount,
|
||||
"exePath": status.ExePath,
|
||||
"isActive": status.IsActive,
|
||||
"updatedAt": status.UpdatedAt,
|
||||
"os": status.OS,
|
||||
"arch": status.Arch,
|
||||
"hostname": status.Hostname,
|
||||
"hostIP": status.HostIP,
|
||||
"cpuUsage": status.CPUUsage,
|
||||
"cpuUsageText": cpuText,
|
||||
"memUsage": status.MemoryUsage,
|
||||
"memUsageText": memText,
|
||||
"load1m": status.Load1m,
|
||||
"load5m": status.Load5m,
|
||||
"load15m": status.Load15m,
|
||||
"buildVersion": status.BuildVersion,
|
||||
"cpuPhysicalCount": status.CPUPhysicalCount,
|
||||
"cpuLogicalCount": status.CPULogicalCount,
|
||||
"exePath": status.ExePath,
|
||||
"apiSuccessPercent": status.APISuccessPercent,
|
||||
"apiAvgCostSeconds": status.APIAvgCostSeconds,
|
||||
}
|
||||
|
||||
@@ -195,7 +195,7 @@ func (this *UpdateAction) RunPost(params struct {
|
||||
|
||||
installDir := strings.TrimSpace(node.GetInstallDir())
|
||||
if len(installDir) == 0 {
|
||||
installDir = "/opt/edge-httpdns"
|
||||
installDir = "/root/edge-httpdns"
|
||||
}
|
||||
|
||||
_, err = this.RPC().HTTPDNSNodeRPC().UpdateHTTPDNSNode(this.AdminContext(), &pb.UpdateHTTPDNSNodeRequest{
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
package clusters
|
||||
package clusters
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/httpdns/httpdnsutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||
"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"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
type ClusterSettingsAction struct {
|
||||
@@ -41,14 +44,15 @@ func (this *ClusterSettingsAction) RunGet(params struct {
|
||||
}
|
||||
|
||||
settings := maps.Map{
|
||||
"name": cluster.GetString("name"),
|
||||
"gatewayDomain": cluster.GetString("gatewayDomain"),
|
||||
"cacheTtl": cluster.GetInt("defaultTTL"),
|
||||
"fallbackTimeout": cluster.GetInt("fallbackTimeout"),
|
||||
"installDir": cluster.GetString("installDir"),
|
||||
"isOn": cluster.GetBool("isOn"),
|
||||
"autoRemoteStart": cluster.GetBool("autoRemoteStart"),
|
||||
"accessLogIsOn": cluster.GetBool("accessLogIsOn"),
|
||||
"name": cluster.GetString("name"),
|
||||
"gatewayDomain": cluster.GetString("gatewayDomain"),
|
||||
"cacheTtl": cluster.GetInt("defaultTTL"),
|
||||
"fallbackTimeout": cluster.GetInt("fallbackTimeout"),
|
||||
"installDir": cluster.GetString("installDir"),
|
||||
"isOn": cluster.GetBool("isOn"),
|
||||
"autoRemoteStart": cluster.GetBool("autoRemoteStart"),
|
||||
"accessLogIsOn": cluster.GetBool("accessLogIsOn"),
|
||||
"timeZone": cluster.GetString("timeZone"),
|
||||
}
|
||||
if settings.GetInt("cacheTtl") <= 0 {
|
||||
settings["cacheTtl"] = 60
|
||||
@@ -57,7 +61,10 @@ func (this *ClusterSettingsAction) RunGet(params struct {
|
||||
settings["fallbackTimeout"] = 300
|
||||
}
|
||||
if len(settings.GetString("installDir")) == 0 {
|
||||
settings["installDir"] = "/opt/edge-httpdns"
|
||||
settings["installDir"] = "/root/edge-httpdns"
|
||||
}
|
||||
if len(settings.GetString("timeZone")) == 0 {
|
||||
settings["timeZone"] = "Asia/Shanghai"
|
||||
}
|
||||
|
||||
listenAddresses := []*serverconfigs.NetworkAddressConfig{
|
||||
@@ -101,19 +108,29 @@ func (this *ClusterSettingsAction) RunGet(params struct {
|
||||
"listen": listenAddresses,
|
||||
"sslPolicy": sslPolicy,
|
||||
}
|
||||
|
||||
this.Data["timeZoneGroups"] = nodeconfigs.FindAllTimeZoneGroups()
|
||||
this.Data["timeZoneLocations"] = nodeconfigs.FindAllTimeZoneLocations()
|
||||
timeZoneStr := settings.GetString("timeZone")
|
||||
if len(timeZoneStr) == 0 {
|
||||
timeZoneStr = nodeconfigs.DefaultTimeZoneLocation
|
||||
}
|
||||
this.Data["timeZoneLocation"] = nodeconfigs.FindTimeZoneLocation(timeZoneStr)
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *ClusterSettingsAction) RunPost(params struct {
|
||||
ClusterId int64
|
||||
Name string
|
||||
GatewayDomain string
|
||||
CacheTtl int32
|
||||
FallbackTimeout int32
|
||||
InstallDir string
|
||||
IsOn bool
|
||||
ClusterId int64
|
||||
Name string
|
||||
GatewayDomain string
|
||||
CacheTtl int32
|
||||
FallbackTimeout int32
|
||||
InstallDir string
|
||||
IsOn bool
|
||||
AutoRemoteStart bool
|
||||
AccessLogIsOn bool
|
||||
AccessLogIsOn bool
|
||||
TimeZone string
|
||||
|
||||
Addresses []byte
|
||||
SslPolicyJSON []byte
|
||||
@@ -129,6 +146,31 @@ func (this *ClusterSettingsAction) RunPost(params struct {
|
||||
params.Must.Field("name", params.Name).Require("请输入集群名称")
|
||||
params.Must.Field("gatewayDomain", params.GatewayDomain).Require("请输入服务域名")
|
||||
|
||||
cluster, err := findClusterMap(this.Parent(), params.ClusterId)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
// 开关项按请求值强制覆盖:未提交/空值都视为 false,支持取消勾选
|
||||
autoRemoteStartRaw := strings.ToLower(strings.TrimSpace(this.ParamString("autoRemoteStart")))
|
||||
params.AutoRemoteStart = autoRemoteStartRaw == "1" || autoRemoteStartRaw == "true" || autoRemoteStartRaw == "on" || autoRemoteStartRaw == "yes" || autoRemoteStartRaw == "enabled"
|
||||
|
||||
accessLogIsOnRaw := strings.ToLower(strings.TrimSpace(this.ParamString("accessLogIsOn")))
|
||||
params.AccessLogIsOn = accessLogIsOnRaw == "1" || accessLogIsOnRaw == "true" || accessLogIsOnRaw == "on" || accessLogIsOnRaw == "yes" || accessLogIsOnRaw == "enabled"
|
||||
|
||||
isOnRaw := strings.ToLower(strings.TrimSpace(this.ParamString("isOn")))
|
||||
params.IsOn = isOnRaw == "1" || isOnRaw == "true" || isOnRaw == "on" || isOnRaw == "yes" || isOnRaw == "enabled"
|
||||
|
||||
// 时区为空时继承当前值,再兜底默认值
|
||||
params.TimeZone = strings.TrimSpace(this.ParamString("timeZone"))
|
||||
if len(params.TimeZone) == 0 {
|
||||
params.TimeZone = strings.TrimSpace(cluster.GetString("timeZone"))
|
||||
}
|
||||
if len(params.TimeZone) == 0 {
|
||||
params.TimeZone = "Asia/Shanghai"
|
||||
}
|
||||
|
||||
if params.CacheTtl <= 0 {
|
||||
params.CacheTtl = 60
|
||||
}
|
||||
@@ -136,20 +178,13 @@ func (this *ClusterSettingsAction) RunPost(params struct {
|
||||
params.FallbackTimeout = 300
|
||||
}
|
||||
if len(params.InstallDir) == 0 {
|
||||
params.InstallDir = "/opt/edge-httpdns"
|
||||
}
|
||||
|
||||
cluster, err := findClusterMap(this.Parent(), params.ClusterId)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
params.InstallDir = "/root/edge-httpdns"
|
||||
}
|
||||
|
||||
tlsConfig := maps.Map{}
|
||||
if rawTLS := strings.TrimSpace(cluster.GetString("tlsPolicyJSON")); len(rawTLS) > 0 {
|
||||
_ = json.Unmarshal([]byte(rawTLS), &tlsConfig)
|
||||
}
|
||||
|
||||
if len(params.Addresses) > 0 {
|
||||
var addresses []*serverconfigs.NetworkAddressConfig
|
||||
if err := json.Unmarshal(params.Addresses, &addresses); err != nil {
|
||||
@@ -158,7 +193,6 @@ func (this *ClusterSettingsAction) RunPost(params struct {
|
||||
}
|
||||
tlsConfig["listen"] = addresses
|
||||
}
|
||||
|
||||
if len(params.SslPolicyJSON) > 0 {
|
||||
sslPolicy := &sslconfigs.SSLPolicy{}
|
||||
if err := json.Unmarshal(params.SslPolicyJSON, sslPolicy); err != nil {
|
||||
@@ -177,7 +211,7 @@ func (this *ClusterSettingsAction) RunPost(params struct {
|
||||
}
|
||||
}
|
||||
|
||||
_, err = this.RPC().HTTPDNSClusterRPC().UpdateHTTPDNSCluster(this.AdminContext(), &pb.UpdateHTTPDNSClusterRequest{
|
||||
updateReq := &pb.UpdateHTTPDNSClusterRequest{
|
||||
ClusterId: params.ClusterId,
|
||||
Name: params.Name,
|
||||
ServiceDomain: params.GatewayDomain,
|
||||
@@ -189,7 +223,16 @@ func (this *ClusterSettingsAction) RunPost(params struct {
|
||||
IsDefault: false,
|
||||
AutoRemoteStart: params.AutoRemoteStart,
|
||||
AccessLogIsOn: params.AccessLogIsOn,
|
||||
})
|
||||
TimeZone: params.TimeZone,
|
||||
}
|
||||
updateCtx := metadata.AppendToOutgoingContext(
|
||||
this.AdminContext(),
|
||||
"x-httpdns-auto-remote-start", fmt.Sprintf("%t", updateReq.GetAutoRemoteStart()),
|
||||
"x-httpdns-access-log-is-on", fmt.Sprintf("%t", updateReq.GetAccessLogIsOn()),
|
||||
"x-httpdns-time-zone", updateReq.GetTimeZone(),
|
||||
)
|
||||
|
||||
_, err = this.RPC().HTTPDNSClusterRPC().UpdateHTTPDNSCluster(updateCtx, updateReq)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package clusters
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
@@ -36,7 +37,7 @@ func (this *CreateAction) RunPost(params struct {
|
||||
params.GatewayDomain = strings.TrimSpace(params.GatewayDomain)
|
||||
params.InstallDir = strings.TrimSpace(params.InstallDir)
|
||||
if len(params.InstallDir) == 0 {
|
||||
params.InstallDir = "/opt/edge-httpdns"
|
||||
params.InstallDir = "/root/edge-httpdns"
|
||||
}
|
||||
if params.CacheTtl <= 0 {
|
||||
params.CacheTtl = 60
|
||||
@@ -56,6 +57,9 @@ func (this *CreateAction) RunPost(params struct {
|
||||
InstallDir: params.InstallDir,
|
||||
IsOn: params.IsOn,
|
||||
IsDefault: false,
|
||||
AutoRemoteStart: true,
|
||||
AccessLogIsOn: true,
|
||||
TimeZone: "Asia/Shanghai",
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
@@ -63,5 +67,12 @@ func (this *CreateAction) RunPost(params struct {
|
||||
}
|
||||
|
||||
this.Data["clusterId"] = resp.GetClusterId()
|
||||
|
||||
// fallback: if frontend JS doesn't intercept form submit, redirect instead of showing raw JSON
|
||||
if len(this.Request.Header.Get("X-Requested-With")) == 0 {
|
||||
this.RedirectURL("/httpdns/clusters/cluster?clusterId=" + strconv.FormatInt(resp.GetClusterId(), 10))
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ func (this *CreateNodeAction) RunPost(params struct {
|
||||
params.InstallDir = strings.TrimSpace(cluster.GetString("installDir"))
|
||||
}
|
||||
if len(params.InstallDir) == 0 {
|
||||
params.InstallDir = "/opt/edge-httpdns"
|
||||
params.InstallDir = "/root/edge-httpdns"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,9 @@ func init() {
|
||||
// Node level
|
||||
GetPost("/createNode", new(CreateNodeAction)).
|
||||
Post("/deleteNode", new(DeleteNodeAction)).
|
||||
Get("/upgradeRemote", new(UpgradeRemoteAction)).
|
||||
GetPost("/cluster/upgradeRemote", new(UpgradeRemoteAction)).
|
||||
GetPost("/upgradeRemote", new(UpgradeRemoteAction)).
|
||||
Post("/upgradeStatus", new(UpgradeStatusAction)).
|
||||
GetPost("/updateNodeSSH", new(UpdateNodeSSHAction)).
|
||||
Post("/checkPorts", new(CheckPortsAction)).
|
||||
|
||||
|
||||
@@ -9,6 +9,10 @@ import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
func listClusterMaps(parent *actionutils.ParentAction, keyword string) ([]maps.Map, error) {
|
||||
@@ -37,6 +41,11 @@ func listClusterMaps(parent *actionutils.ParentAction, keyword string) ([]maps.M
|
||||
}
|
||||
}
|
||||
|
||||
countUpgradeNodes, err := countUpgradeHTTPDNSNodes(parent, cluster.GetId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
port := "443"
|
||||
if rawTLS := cluster.GetTlsPolicyJSON(); len(rawTLS) > 0 {
|
||||
tlsConfig := maps.Map{}
|
||||
@@ -56,32 +65,87 @@ func listClusterMaps(parent *actionutils.ParentAction, keyword string) ([]maps.M
|
||||
apiAddress := "https://" + cluster.GetServiceDomain() + ":" + port
|
||||
|
||||
result = append(result, maps.Map{
|
||||
"id": cluster.GetId(),
|
||||
"name": cluster.GetName(),
|
||||
"gatewayDomain": cluster.GetServiceDomain(),
|
||||
"apiAddress": apiAddress,
|
||||
"defaultTTL": cluster.GetDefaultTTL(),
|
||||
"fallbackTimeout": cluster.GetFallbackTimeoutMs(),
|
||||
"installDir": cluster.GetInstallDir(),
|
||||
"isOn": cluster.GetIsOn(),
|
||||
"isDefault": cluster.GetIsDefault(),
|
||||
"countAllNodes": countAllNodes,
|
||||
"countActiveNodes": countActiveNodes,
|
||||
"id": cluster.GetId(),
|
||||
"name": cluster.GetName(),
|
||||
"gatewayDomain": cluster.GetServiceDomain(),
|
||||
"apiAddress": apiAddress,
|
||||
"defaultTTL": cluster.GetDefaultTTL(),
|
||||
"fallbackTimeout": cluster.GetFallbackTimeoutMs(),
|
||||
"installDir": cluster.GetInstallDir(),
|
||||
"timeZone": cluster.GetTimeZone(),
|
||||
"isOn": cluster.GetIsOn(),
|
||||
"isDefault": cluster.GetIsDefault(),
|
||||
"autoRemoteStart": cluster.GetAutoRemoteStart(),
|
||||
"accessLogIsOn": cluster.GetAccessLogIsOn(),
|
||||
"tlsPolicyJSON": cluster.GetTlsPolicyJSON(),
|
||||
"countAllNodes": countAllNodes,
|
||||
"countActiveNodes": countActiveNodes,
|
||||
"countUpgradeNodes": countUpgradeNodes,
|
||||
})
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func countUpgradeHTTPDNSNodes(parent *actionutils.ParentAction, clusterID int64) (int64, error) {
|
||||
countResp, err := parent.RPC().HTTPDNSNodeRPC().CountAllUpgradeHTTPDNSNodesWithClusterId(parent.AdminContext(), &pb.CountAllUpgradeHTTPDNSNodesWithClusterIdRequest{
|
||||
ClusterId: clusterID,
|
||||
})
|
||||
if err == nil {
|
||||
return countResp.GetCount(), nil
|
||||
}
|
||||
|
||||
grpcStatus, ok := status.FromError(err)
|
||||
if !ok || grpcStatus.Code() != codes.Unimplemented {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Compatibility fallback: old edge-api may not implement countAllUpgradeHTTPDNSNodesWithClusterId yet.
|
||||
listResp, listErr := parent.RPC().HTTPDNSNodeRPC().FindAllUpgradeHTTPDNSNodesWithClusterId(parent.AdminContext(), &pb.FindAllUpgradeHTTPDNSNodesWithClusterIdRequest{
|
||||
ClusterId: clusterID,
|
||||
})
|
||||
if listErr == nil {
|
||||
return int64(len(listResp.GetNodes())), nil
|
||||
}
|
||||
|
||||
listStatus, ok := status.FromError(listErr)
|
||||
if ok && listStatus.Code() == codes.Unimplemented {
|
||||
// Compatibility fallback: both methods missing on old edge-api, don't block page rendering.
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
return 0, listErr
|
||||
}
|
||||
|
||||
func findClusterMap(parent *actionutils.ParentAction, clusterID int64) (maps.Map, error) {
|
||||
if clusterID > 0 {
|
||||
var headerMD metadata.MD
|
||||
resp, err := parent.RPC().HTTPDNSClusterRPC().FindHTTPDNSCluster(parent.AdminContext(), &pb.FindHTTPDNSClusterRequest{
|
||||
ClusterId: clusterID,
|
||||
})
|
||||
}, grpc.Header(&headerMD))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.GetCluster() != nil {
|
||||
cluster := resp.GetCluster()
|
||||
autoRemoteStart := cluster.GetAutoRemoteStart()
|
||||
accessLogIsOn := cluster.GetAccessLogIsOn()
|
||||
timeZone := cluster.GetTimeZone()
|
||||
|
||||
// Compatibility fallback:
|
||||
// Some deployed admin binaries may decode newly-added protobuf fields incorrectly.
|
||||
// Read values from grpc response headers as a source of truth.
|
||||
if values := headerMD.Get("x-httpdns-auto-remote-start"); len(values) > 0 {
|
||||
autoRemoteStart = parseBoolLike(values[0], autoRemoteStart)
|
||||
}
|
||||
if values := headerMD.Get("x-httpdns-access-log-is-on"); len(values) > 0 {
|
||||
accessLogIsOn = parseBoolLike(values[0], accessLogIsOn)
|
||||
}
|
||||
if values := headerMD.Get("x-httpdns-time-zone"); len(values) > 0 {
|
||||
if tz := strings.TrimSpace(values[0]); len(tz) > 0 {
|
||||
timeZone = tz
|
||||
}
|
||||
}
|
||||
|
||||
return maps.Map{
|
||||
"id": cluster.GetId(),
|
||||
"name": cluster.GetName(),
|
||||
@@ -92,8 +156,9 @@ func findClusterMap(parent *actionutils.ParentAction, clusterID int64) (maps.Map
|
||||
"isOn": cluster.GetIsOn(),
|
||||
"isDefault": cluster.GetIsDefault(),
|
||||
"tlsPolicyJSON": cluster.GetTlsPolicyJSON(),
|
||||
"autoRemoteStart": cluster.GetAutoRemoteStart(),
|
||||
"accessLogIsOn": cluster.GetAccessLogIsOn(),
|
||||
"autoRemoteStart": autoRemoteStart,
|
||||
"accessLogIsOn": accessLogIsOn,
|
||||
"timeZone": timeZone,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
@@ -109,12 +174,25 @@ func findClusterMap(parent *actionutils.ParentAction, clusterID int64) (maps.Map
|
||||
"gatewayDomain": "",
|
||||
"defaultTTL": 60,
|
||||
"fallbackTimeout": 300,
|
||||
"installDir": "/opt/edge-httpdns",
|
||||
"installDir": "/root/edge-httpdns",
|
||||
"timeZone": "Asia/Shanghai",
|
||||
}, nil
|
||||
}
|
||||
return clusters[0], nil
|
||||
}
|
||||
|
||||
func parseBoolLike(raw string, defaultValue bool) bool {
|
||||
s := strings.ToLower(strings.TrimSpace(raw))
|
||||
switch s {
|
||||
case "1", "true", "on", "yes", "enabled":
|
||||
return true
|
||||
case "0", "false", "off", "no", "disabled":
|
||||
return false
|
||||
default:
|
||||
return defaultValue
|
||||
}
|
||||
}
|
||||
|
||||
func listNodeMaps(parent *actionutils.ParentAction, clusterID int64) ([]maps.Map, error) {
|
||||
resp, err := parent.RPC().HTTPDNSNodeRPC().ListHTTPDNSNodes(parent.AdminContext(), &pb.ListHTTPDNSNodesRequest{
|
||||
ClusterId: clusterID,
|
||||
@@ -132,21 +210,21 @@ func listNodeMaps(parent *actionutils.ParentAction, clusterID int64) ([]maps.Map
|
||||
ip = parsed
|
||||
}
|
||||
nodeMap := maps.Map{
|
||||
"id": node.GetId(),
|
||||
"clusterId": node.GetClusterId(),
|
||||
"name": node.GetName(),
|
||||
"isOn": node.GetIsOn(),
|
||||
"isUp": node.GetIsUp(),
|
||||
"isInstalled": node.GetIsInstalled(),
|
||||
"isActive": node.GetIsActive(),
|
||||
"installDir": node.GetInstallDir(),
|
||||
"uniqueId": node.GetUniqueId(),
|
||||
"secret": node.GetSecret(),
|
||||
"status": statusMap,
|
||||
"id": node.GetId(),
|
||||
"clusterId": node.GetClusterId(),
|
||||
"name": node.GetName(),
|
||||
"isOn": node.GetIsOn(),
|
||||
"isUp": node.GetIsUp(),
|
||||
"isInstalled": node.GetIsInstalled(),
|
||||
"isActive": node.GetIsActive(),
|
||||
"installDir": node.GetInstallDir(),
|
||||
"uniqueId": node.GetUniqueId(),
|
||||
"secret": node.GetSecret(),
|
||||
"status": statusMap,
|
||||
"installStatus": installStatusMap,
|
||||
"region": nil,
|
||||
"login": nil,
|
||||
"apiNodeAddrs": []string{},
|
||||
"region": nil,
|
||||
"login": nil,
|
||||
"apiNodeAddrs": []string{},
|
||||
"cluster": maps.Map{
|
||||
"id": node.GetClusterId(),
|
||||
"installDir": node.GetInstallDir(),
|
||||
@@ -227,21 +305,21 @@ func decodeNodeStatus(raw []byte) maps.Map {
|
||||
cpuText := fmt.Sprintf("%.2f%%", status.CPUUsage*100)
|
||||
memText := fmt.Sprintf("%.2f%%", status.MemoryUsage*100)
|
||||
return maps.Map{
|
||||
"isActive": status.IsActive,
|
||||
"updatedAt": status.UpdatedAt,
|
||||
"hostname": status.Hostname,
|
||||
"hostIP": status.HostIP,
|
||||
"cpuUsage": status.CPUUsage,
|
||||
"cpuUsageText": cpuText,
|
||||
"memUsage": status.MemoryUsage,
|
||||
"memUsageText": memText,
|
||||
"load1m": status.Load1m,
|
||||
"load5m": status.Load5m,
|
||||
"load15m": status.Load15m,
|
||||
"buildVersion": status.BuildVersion,
|
||||
"isActive": status.IsActive,
|
||||
"updatedAt": status.UpdatedAt,
|
||||
"hostname": status.Hostname,
|
||||
"hostIP": status.HostIP,
|
||||
"cpuUsage": status.CPUUsage,
|
||||
"cpuUsageText": cpuText,
|
||||
"memUsage": status.MemoryUsage,
|
||||
"memUsageText": memText,
|
||||
"load1m": status.Load1m,
|
||||
"load5m": status.Load5m,
|
||||
"load15m": status.Load15m,
|
||||
"buildVersion": status.BuildVersion,
|
||||
"cpuPhysicalCount": status.CPUPhysicalCount,
|
||||
"cpuLogicalCount": status.CPULogicalCount,
|
||||
"exePath": status.ExePath,
|
||||
"cpuLogicalCount": status.CPULogicalCount,
|
||||
"exePath": status.ExePath,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,108 @@
|
||||
package clusters
|
||||
|
||||
import "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/httpdns/httpdnsutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
type UpgradeRemoteAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpgradeRemoteAction) Init() {
|
||||
this.Nav("httpdns", "cluster", "index")
|
||||
}
|
||||
|
||||
func (this *UpgradeRemoteAction) RunGet(params struct {
|
||||
NodeId int64
|
||||
ClusterId int64
|
||||
}) {
|
||||
this.Data["nodeId"] = params.NodeId
|
||||
httpdnsutils.AddLeftMenu(this.Parent())
|
||||
|
||||
cluster, err := findClusterMap(this.Parent(), params.ClusterId)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
httpdnsutils.AddClusterTabbar(this.Parent(), cluster.GetString("name"), params.ClusterId, "node")
|
||||
|
||||
this.Data["clusterId"] = params.ClusterId
|
||||
this.Data["cluster"] = cluster
|
||||
|
||||
resp, err := this.RPC().HTTPDNSNodeRPC().FindAllUpgradeHTTPDNSNodesWithClusterId(this.AdminContext(), &pb.FindAllUpgradeHTTPDNSNodesWithClusterIdRequest{
|
||||
ClusterId: params.ClusterId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
nodes := make([]maps.Map, 0, len(resp.GetNodes()))
|
||||
for _, upgradeNode := range resp.GetNodes() {
|
||||
node := upgradeNode.Node
|
||||
if node == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
loginParams := maps.Map{}
|
||||
if node.GetNodeLogin() != nil && len(node.GetNodeLogin().GetParams()) > 0 {
|
||||
_ = json.Unmarshal(node.GetNodeLogin().GetParams(), &loginParams)
|
||||
}
|
||||
|
||||
status := decodeNodeStatus(node.GetStatusJSON())
|
||||
accessIP := strings.TrimSpace(status.GetString("hostIP"))
|
||||
if len(accessIP) == 0 {
|
||||
accessIP = strings.TrimSpace(node.GetName())
|
||||
}
|
||||
|
||||
nodes = append(nodes, maps.Map{
|
||||
"id": node.GetId(),
|
||||
"name": node.GetName(),
|
||||
"accessIP": accessIP,
|
||||
"oldVersion": upgradeNode.OldVersion,
|
||||
"newVersion": upgradeNode.NewVersion,
|
||||
"login": node.GetNodeLogin(),
|
||||
"loginParams": loginParams,
|
||||
"installStatus": decodeUpgradeInstallStatus(node.GetInstallStatusJSON()),
|
||||
})
|
||||
}
|
||||
this.Data["nodes"] = nodes
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *UpgradeRemoteAction) RunPost(params struct {
|
||||
NodeId int64
|
||||
|
||||
Must *actions.Must
|
||||
}) {
|
||||
_, err := this.RPC().HTTPDNSNodeRPC().UpgradeHTTPDNSNode(this.AdminContext(), &pb.UpgradeHTTPDNSNodeRequest{
|
||||
NodeId: params.NodeId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.Success()
|
||||
}
|
||||
|
||||
func decodeUpgradeInstallStatus(raw []byte) maps.Map {
|
||||
result := maps.Map{
|
||||
"isRunning": false,
|
||||
"isFinished": false,
|
||||
"isOk": false,
|
||||
"error": "",
|
||||
"errorCode": "",
|
||||
}
|
||||
if len(raw) == 0 {
|
||||
return result
|
||||
}
|
||||
|
||||
_ = json.Unmarshal(raw, &result)
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package clusters
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
)
|
||||
|
||||
type UpgradeStatusAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpgradeStatusAction) RunPost(params struct {
|
||||
NodeId int64
|
||||
}) {
|
||||
resp, err := this.RPC().HTTPDNSNodeRPC().FindHTTPDNSNode(this.AdminContext(), &pb.FindHTTPDNSNodeRequest{
|
||||
NodeId: params.NodeId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if resp.GetNode() == nil {
|
||||
this.Data["status"] = nil
|
||||
this.Success()
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["status"] = decodeUpgradeInstallStatusMap(resp.GetNode().GetInstallStatusJSON())
|
||||
this.Success()
|
||||
}
|
||||
|
||||
func decodeUpgradeInstallStatusMap(raw []byte) map[string]interface{} {
|
||||
result := map[string]interface{}{
|
||||
"isRunning": false,
|
||||
"isFinished": false,
|
||||
"isOk": false,
|
||||
"error": "",
|
||||
"errorCode": "",
|
||||
}
|
||||
if len(raw) == 0 {
|
||||
return result
|
||||
}
|
||||
|
||||
_ = json.Unmarshal(raw, &result)
|
||||
return result
|
||||
}
|
||||
Reference in New Issue
Block a user