feat: sync httpdns sdk/platform updates without large binaries

This commit is contained in:
robin
2026-03-04 17:59:14 +08:00
parent 853897a6f8
commit 532891fad0
700 changed files with 6096 additions and 2712 deletions

View File

@@ -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

View File

@@ -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")

View File

@@ -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
}

View File

@@ -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("不支持的平台,可选值:androidiosflutter")
}
}
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")
}

View File

@@ -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"
}

View File

@@ -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()
}