mirror of
https://github.com/yusing/godoxy.git
synced 2026-04-20 07:21:26 +02:00
Feat/fileserver (#60)
* cleanup code for URL type * fix makefile for trace mode * refactor, merge Entry, RawEntry and Route into one. * Implement fileserver. * refactor: rename HTTPRoute to ReverseProxyRoute to avoid confusion * refactor: move metrics logger to middleware package - fix prometheus metrics for load balanced routes - route will now fail when health monitor fail to start * fix extra output of ls-* commands by defer initializaing stuff, speed up start time * add test for path traversal attack, small fix on FileServer.Start method * rename rule.on.bypass to pass * refactor and fixed map-to-map deserialization * updated route loading logic * schemas: add "add_prefix" option to modify_request middleware * updated route JSONMarshalling --------- Co-authored-by: yusing <yusing@6uo.me>
This commit is contained in:
@@ -31,6 +31,13 @@ var (
|
||||
ErrUnknownField = E.New("unknown field")
|
||||
)
|
||||
|
||||
var (
|
||||
tagDeserialize = "deserialize" // `deserialize:"-"` to exclude from deserialization
|
||||
tagJSON = "json" // share between Deserialize and json.Marshal
|
||||
tagValidate = "validate" // uses go-playground/validator
|
||||
tagAliases = "aliases" // declare aliases for fields
|
||||
)
|
||||
|
||||
var mapUnmarshalerType = reflect.TypeFor[MapUnmarshaller]()
|
||||
|
||||
var defaultValues = functional.NewMapOf[reflect.Type, func() any]()
|
||||
@@ -67,6 +74,9 @@ func extractFields(t reflect.Type) (all, anonymous []reflect.StructField) {
|
||||
if !field.IsExported() {
|
||||
continue
|
||||
}
|
||||
if field.Tag.Get(tagDeserialize) == "-" {
|
||||
continue
|
||||
}
|
||||
if field.Anonymous {
|
||||
f1, f2 := extractFields(field.Type)
|
||||
fields = append(fields, f1...)
|
||||
@@ -97,6 +107,33 @@ func ValidateWithFieldTags(s any) E.Error {
|
||||
return errs.Error()
|
||||
}
|
||||
|
||||
func ValidateWithCustomValidator(v reflect.Value) E.Error {
|
||||
isStruct := false
|
||||
for {
|
||||
switch v.Kind() {
|
||||
case reflect.Pointer, reflect.Interface:
|
||||
if v.IsNil() {
|
||||
return E.Errorf("validate: v is %w", ErrNilValue)
|
||||
}
|
||||
if validate, ok := v.Interface().(CustomValidator); ok {
|
||||
return validate.Validate()
|
||||
}
|
||||
if isStruct {
|
||||
return nil
|
||||
}
|
||||
v = v.Elem()
|
||||
case reflect.Struct:
|
||||
if !v.CanAddr() {
|
||||
return nil
|
||||
}
|
||||
v = v.Addr()
|
||||
isStruct = true
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func dive(dst reflect.Value) (v reflect.Value, t reflect.Type, err E.Error) {
|
||||
dstT := dst.Type()
|
||||
for {
|
||||
@@ -186,7 +223,7 @@ func Deserialize(src SerializedObject, dst any) (err E.Error) {
|
||||
}
|
||||
for _, field := range fields {
|
||||
var key string
|
||||
if jsonTag, ok := field.Tag.Lookup("json"); ok {
|
||||
if jsonTag, ok := field.Tag.Lookup(tagJSON); ok {
|
||||
if jsonTag == "-" {
|
||||
continue
|
||||
}
|
||||
@@ -198,10 +235,10 @@ func Deserialize(src SerializedObject, dst any) (err E.Error) {
|
||||
mapping[key] = dstV.FieldByName(field.Name)
|
||||
|
||||
if !hasValidateTag {
|
||||
_, hasValidateTag = field.Tag.Lookup("validate")
|
||||
_, hasValidateTag = field.Tag.Lookup(tagValidate)
|
||||
}
|
||||
|
||||
aliases, ok := field.Tag.Lookup("aliases")
|
||||
aliases, ok := field.Tag.Lookup(tagAliases)
|
||||
if ok {
|
||||
for _, alias := range strutils.CommaSeperatedList(aliases) {
|
||||
mapping[alias] = dstV.FieldByName(field.Name)
|
||||
@@ -220,34 +257,28 @@ func Deserialize(src SerializedObject, dst any) (err E.Error) {
|
||||
}
|
||||
if hasValidateTag {
|
||||
errs.Add(ValidateWithFieldTags(dstV.Interface()))
|
||||
} else {
|
||||
if dstV.CanAddr() {
|
||||
dstV = dstV.Addr()
|
||||
}
|
||||
if validator, ok := dstV.Interface().(CustomValidator); ok {
|
||||
errs.Add(validator.Validate())
|
||||
}
|
||||
}
|
||||
if err := ValidateWithCustomValidator(dstV); err != nil {
|
||||
errs.Add(err)
|
||||
}
|
||||
return errs.Error()
|
||||
case reflect.Map:
|
||||
if dstV.IsNil() {
|
||||
dstV.Set(reflect.MakeMap(dstT))
|
||||
}
|
||||
for k := range src {
|
||||
for k, v := range src {
|
||||
mapVT := dstT.Elem()
|
||||
tmp := New(mapVT).Elem()
|
||||
err := Convert(reflect.ValueOf(src[k]), tmp)
|
||||
if err == nil {
|
||||
dstV.SetMapIndex(reflect.ValueOf(k), tmp)
|
||||
} else {
|
||||
err := Convert(reflect.ValueOf(v), tmp)
|
||||
if err != nil {
|
||||
errs.Add(err.Subject(k))
|
||||
continue
|
||||
}
|
||||
if err := ValidateWithCustomValidator(tmp.Addr()); err != nil {
|
||||
errs.Add(err.Subject(k))
|
||||
} else {
|
||||
dstV.SetMapIndex(reflect.ValueOf(k), tmp)
|
||||
}
|
||||
}
|
||||
if dstV.CanAddr() {
|
||||
dstV = dstV.Addr()
|
||||
}
|
||||
if validator, ok := dstV.Interface().(CustomValidator); ok {
|
||||
errs.Add(validator.Validate())
|
||||
if err := ValidateWithCustomValidator(dstV); err != nil {
|
||||
errs.Add(err)
|
||||
}
|
||||
return errs.Error()
|
||||
default:
|
||||
|
||||
@@ -16,7 +16,10 @@ func init() {
|
||||
}
|
||||
}
|
||||
|
||||
func IgnoreError[Result any](r Result, _ error) Result {
|
||||
func Must[Result any](r Result, err error) Result {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user