|
|
|
|
@@ -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,43 +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 + "/EdgeUser/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")
|
|
|
|
|
}
|
|
|
|
|
|