]> git.uzoombox.com Git - uzsdk-v0.3.4.git/commitdiff
feat(utils): 1. add convutil.SortSlice. master v0.3.4
authorbruce <bruce@nanzoom.com>
Wed, 29 Oct 2025 02:50:41 +0000 (02:50 +0000)
committerbruce <bruce@nanzoom.com>
Wed, 29 Oct 2025 02:50:41 +0000 (02:50 +0000)
feat(uzsdk): 1. add Configuration.Backup.
  2. default Configuration.Service.TrustedProxies to ["127.0.0.1", "192.168.8.1"].

utils/convutil/sort.go
utils/utils.sh
uzsdk/configuration.go
version.go

index cb686f3210f5a08584fd6c15cbe2ab225d804883..f1786c37256b76dc4b77a6895b801ce4be39e9c5 100644 (file)
@@ -1,10 +1,13 @@
-// Copyright 2010-2022 nanzoom.com. All Rights Reserved.
+// Copyright 2010-2025 nanzoom.com. All Rights Reserved.
 
 package convutil
 
 import (
+       "fmt"
        "reflect"
        "sort"
+       "strings"
+       "time"
 )
 
 // SortMap returns an sorted array of map keys.
@@ -93,3 +96,104 @@ func SortMapString(v any) []string {
        sort.Strings(keys)
        return keys
 }
+
+// SortSlice sorts a slice of structs based on a field name and order.
+// It now supports basic types, time.Time, and pointers to basic types.
+// slicePtr: A pointer to the slice of structs to be sorted.
+// fieldName: The name of the struct field to sort by (must be exported).
+// ascending: true for ascending order, false for descending order.
+func SortSlice(slicePtr any, fieldName string, ascending bool) error {
+       // 1. 使用反射获取切片的值
+       sliceVal := reflect.ValueOf(slicePtr)
+       if sliceVal.Kind() != reflect.Ptr {
+               return fmt.Errorf("must provide a pointer to a slice")
+       }
+       sliceVal = sliceVal.Elem()
+       if sliceVal.Kind() != reflect.Slice {
+               return fmt.Errorf("must provide a pointer to a slice")
+       }
+
+       // 2. 检查切片是否为空
+       if sliceVal.Len() == 0 {
+               return nil // 空切片无需排序
+       }
+
+       // 3. 验证字段是否存在
+       firstElem := sliceVal.Index(0)
+       if firstElem.Kind() != reflect.Struct {
+               return fmt.Errorf("slice elements must be structs")
+       }
+       field := firstElem.FieldByName(fieldName)
+       if !field.IsValid() {
+               return fmt.Errorf("field '%s' not found in struct", fieldName)
+       }
+
+       // 4. 使用 sort.Slice 进行排序
+       sort.Slice(sliceVal.Interface(), func(i, j int) bool {
+               // 获取第 i 个和第 j 个元素的指定字段
+               fieldI := sliceVal.Index(i).FieldByName(fieldName)
+               fieldJ := sliceVal.Index(j).FieldByName(fieldName)
+
+               // 比较函数
+               isLess := func() bool {
+                       // --- 处理指针类型 ---
+                       if fieldI.Kind() == reflect.Ptr {
+                               // 规则1: 处理 nil 值
+                               isNilI := fieldI.IsNil()
+                               isNilJ := fieldJ.IsNil()
+
+                               if isNilI && isNilJ {
+                                       return false // 两个都为 nil,视为相等,不交换
+                               }
+                               if isNilI { // i 是 nil, j 不是
+                                       return true // nil 值排在前面 (升序)
+                               }
+                               if isNilJ { // j 是 nil, i 不是
+                                       return false
+                               }
+
+                               // 规则2: 两个指针都不为 nil,解引用后比较
+                               elemI := fieldI.Elem()
+                               elemJ := fieldJ.Elem()
+                               // 现在 elemI 和 elemJ 是指针指向的实际值,继续下面的 switch 逻辑
+                               return compareValues(elemI, elemJ)
+                       }
+
+                       // --- 处理非指针类型 ---
+                       return compareValues(fieldI, fieldJ)
+               }()
+
+               // 根据排序方向返回结果
+               if ascending {
+                       return isLess
+               }
+               return !isLess
+       })
+
+       return nil
+}
+
+// compareValues is a helper function to compare two reflect.Value.
+// It assumes the values are of the same comparable kind.
+func compareValues(valI, valJ reflect.Value) bool {
+       switch valI.Kind() {
+       case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+               return valI.Int() < valJ.Int()
+       case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+               return valI.Uint() < valJ.Uint()
+       case reflect.Float32, reflect.Float64:
+               return valI.Float() < valJ.Float()
+       case reflect.String:
+               return strings.Compare(valI.String(), valJ.String()) < 0
+       case reflect.Struct:
+               // 特别处理 time.Time 类型
+               if t, ok := valI.Interface().(time.Time); ok {
+                       return t.Before(valJ.Interface().(time.Time))
+               }
+               // 其他结构体不支持比较
+               return false
+       default:
+               // 对于不支持的类型,默认不交换顺序
+               return false
+       }
+}
index cb12b6ef652a43b6f5a3dbfa4ed6bba8ed4a1795..374e3e3dda5b6f66f8c8257d38269dc9862808c7 100644 (file)
@@ -108,4 +108,4 @@ function exportTableDict() {
 }
 
 # generateSbs "$@"
-generateStruct "$@"
\ No newline at end of file
+generateStruct "$@"
index 0517d099437df82bfb62c55c78ab0a05a860f370..6de0f55c66fc324a17f90ca552ea21173c3ff587 100644 (file)
@@ -17,7 +17,7 @@ type Configuration struct {
                PublicName     string   // smartcloud address served by httpd, default: [ServerName], exp: http://192.168.0.6:8080
                ServicePath    string   // urlpath for this service, default: /wservice
                DataDirectory  string   // server data directory, default: /data/cloud
-               TrustedProxies []string // a list of proxies trusted, default: ["127.0.0.1"]
+               TrustedProxies []string // a list of proxies trusted, default: ["127.0.0.1", "192.168.8.1"]
        }
        System struct {
                DBDriver   string // database driver, default: postgres
@@ -32,6 +32,22 @@ type Configuration struct {
                DbgOutput  bool   // debug: output more details, default: fasle.
                DbgToken   bool   // debug: use token '63886e35-7291-4b4d-a66f-fa7431542ace', default: false
        }
+       Backup struct {
+               Resource string // default: [schema]://[user]:[password]@[host]/[bucket]?sslmode=[sslmode]
+               Storage  struct {
+                       Scheme   string // schema, default: minio
+                       User     string // user, default: 1130787647532715
+                       Password string // password, default: 7NAIzs5xZoyOGVTC33Fu3psJgXVs4Ws
+                       Host     string // host, default: 127.0.0.1:9009
+                       Bucket   string // bucket, default: backup
+                       Sslmode  bool   // sslmode default: false
+               }
+               Notification struct {
+                       Method   string // default: webhook
+                       Endpoint string // default: http://127.0.0.1:9000/wservice/storage/event
+                       Arn      string // default: arn:minio:sqs::uzoombox:webhook
+               }
+       }
        GZip struct {
                Enable bool  // enable gzip with gin, default: true
                Level  int64 // gzip compress level, default: gzip.DefaultCompression
@@ -72,10 +88,10 @@ func (setting *Configuration) Check() {
                setting.Service.DataDirectory = "/data/uzoombox/cloud"
        }
        if len(setting.Service.TrustedProxies) == 0 {
-               setting.Service.TrustedProxies = append(setting.Service.TrustedProxies, "127.0.0.1")
+               setting.Service.TrustedProxies = append(setting.Service.TrustedProxies, "127.0.0.1", "192.168.8.1")
        }
 
-       // // Configuration.System
+       // Configuration.System
        if setting.System.DBDriver == "" {
                setting.System.DBDriver = "postgres"
        }
@@ -106,6 +122,39 @@ func (setting *Configuration) Check() {
                setting.System.CheckTime = (3600 >> 1)
        }
 
+       // Configuration.Backup
+       if setting.Backup.Storage.Scheme == "" {
+               setting.Backup.Storage.Scheme = "minio"
+       }
+       if setting.Backup.Storage.User == "" {
+               setting.Backup.Storage.User = "1130787647532715"
+       }
+       if setting.Backup.Storage.Password == "" {
+               setting.Backup.Storage.Password = "7NAIzs5xZoyOGVTC33Fu3psJgXVs4Ws"
+       }
+       if setting.Backup.Storage.Host == "" {
+               setting.Backup.Storage.Host = "127.0.0.1:9009"
+       }
+       if setting.Backup.Storage.Bucket == "" {
+               setting.Backup.Storage.Bucket = "backup"
+       }
+       if !setting.Backup.Storage.Sslmode {
+               setting.Backup.Storage.Sslmode = false
+       }
+       storage := &setting.Backup.Storage
+       setting.Backup.Resource = fmt.Sprintf("%v://%v:%v@%v/%v?sslmode=%v",
+               storage.Scheme, storage.User, storage.Password, storage.Host, storage.Bucket, storage.Sslmode)
+
+       if setting.Backup.Notification.Method == "" {
+               setting.Backup.Notification.Method = "webhook"
+       }
+       if setting.Backup.Notification.Endpoint == "" {
+               setting.Backup.Notification.Endpoint = setting.Service.ServerName + setting.Service.ServicePath + "/storage/event"
+       }
+       if setting.Backup.Notification.Arn == "" {
+               setting.Backup.Notification.Arn = fmt.Sprintf("arn:minio:sqs::uzoombox:%v", setting.Backup.Notification.Method)
+       }
+
        // Configuration.GZip
        if setting.GZip.Enable {
                setting.GZip.Enable = true
index f453987cee9329c2e561bf3b7e3f4957d1c2f271..4e4dc57c4ca97e93b52155a5d759a8584533fe00 100644 (file)
@@ -2,4 +2,4 @@
 package uzsdk
 
 // Version is the current opensdk's version.
-const Version = "0.3.3"
+const Version = "0.3.4"