feat: addded scanning for the PHP shit.
This commit is contained in:
parent
8ea98e8586
commit
f410b16797
4 changed files with 270 additions and 154 deletions
|
@ -2,18 +2,79 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"vultras.su/core/bond"
|
"vultras.su/core/bond"
|
||||||
|
"vultras.su/core/bond/urlenc"
|
||||||
//"vultras.su/core/bond/methods"
|
//"vultras.su/core/bond/methods"
|
||||||
"vultras.su/core/bond/statuses"
|
"vultras.su/core/bond/statuses"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
//"io"
|
||||||
"net/url"
|
//"net/url"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type JsonInt64 int64
|
||||||
|
func(ji *JsonInt) UnmarshalJSON(bts []byte) error {
|
||||||
|
k, err := strconv.ParseInt(string(bts), 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type JsonArrayMap[V any] map[int] V
|
||||||
|
func (jam *JsonArrayMap[V]) UnmarshalJSON(bts []byte) error {
|
||||||
|
*jam = make(JsonArrayMap[V])
|
||||||
|
am := *jam
|
||||||
|
j := map[string] json.RawMessage{}
|
||||||
|
err := json.Unmarshal(bts, &j)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
v := new(V)
|
||||||
|
for jk, jv := range j {
|
||||||
|
// Getting the key from string.
|
||||||
|
k, err := strconv.ParseInt(jk, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal([]byte(jv), v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
am[int(k)] = *v
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type GetNotesOptions struct {
|
type GetNotesOptions struct {
|
||||||
Id int `json:"id"`
|
Id int `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
}
|
}
|
||||||
|
type WebhookRequest struct {
|
||||||
|
Leads Leads `json:"leads"`
|
||||||
|
Account Account `json:"account"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Leads struct {
|
||||||
|
Status JsonArrayMap[Status]`json:"status"`
|
||||||
|
Add JsonArrayMap[Status] `json:"add"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Status struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
StatusId string `json:"status_id"`
|
||||||
|
PipelineId string `json:"pipeline_id"`
|
||||||
|
OldStatusId string `json:"old_status_id"`
|
||||||
|
OldPipelineId string `json:"old_pipeline_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Account struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
SubDomain string `json:"subdomain"`
|
||||||
|
}
|
||||||
|
|
||||||
var root = bond.Root(bond.Path().
|
var root = bond.Root(bond.Path().
|
||||||
Def(
|
Def(
|
||||||
|
@ -21,7 +82,14 @@ Def(
|
||||||
/*bond.Method().Def(
|
/*bond.Method().Def(
|
||||||
methods.Post,*/
|
methods.Post,*/
|
||||||
bond.Func(func(c *bond.Context){
|
bond.Func(func(c *bond.Context){
|
||||||
fmt.Printf("Content-Type: %q\n", c.ContentType())
|
reciever := WebhookRequest{}
|
||||||
|
c.Scan(&reciever)
|
||||||
|
if c.ScanErr() != nil {
|
||||||
|
fmt.Printf("err: %s\n", c.ScanErr())
|
||||||
|
}
|
||||||
|
fmt.Printf("%#v\n", reciever)
|
||||||
|
return
|
||||||
|
/*fmt.Printf("Content-Type: %q\n", c.ContentType())
|
||||||
body, err := io.ReadAll(c.R.Body)
|
body, err := io.ReadAll(c.R.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("err:%s\n", err)
|
fmt.Printf("err:%s\n", err)
|
||||||
|
@ -45,19 +113,32 @@ Def(
|
||||||
fmt.Printf("err:%s\n", err)
|
fmt.Printf("err:%s\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.Printf("\nparsed: %v\n", string(js))
|
fmt.Printf("\nparsed: %v\n", string(js))*/
|
||||||
c.SetStatus(statuses.OK)
|
c.SetStatus(statuses.OK)
|
||||||
}),
|
}),
|
||||||
//),
|
//),
|
||||||
))
|
))
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
srv := bond.Server{
|
requestString := "leads[status][0][id]=2050297&" +
|
||||||
|
"leads[status][0][status_id]=35573056&" +
|
||||||
|
"leads[status][0][pipeline_id]=3643927&" +
|
||||||
|
"leads[status][0][old_status_id]=35572897&" +
|
||||||
|
"leads[status][0][old_pipeline_id]=3643927&" +
|
||||||
|
"account[id]=29085955&" +
|
||||||
|
"account[subdomain]=domain"
|
||||||
|
reciever := WebhookRequest{}
|
||||||
|
err := urlenc.Unmarshal([]byte(requestString), &reciever)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Printf("%#v\n", reciever)
|
||||||
|
/*srv := bond.Server{
|
||||||
Addr: ":15080",
|
Addr: ":15080",
|
||||||
Handler: root,
|
Handler: root,
|
||||||
}
|
}
|
||||||
err := srv.ListenAndServe()
|
err := srv.ListenAndServe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
|
13
context.go
13
context.go
|
@ -7,6 +7,7 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"fmt"
|
"fmt"
|
||||||
"vultras.su/core/bond/contents"
|
"vultras.su/core/bond/contents"
|
||||||
|
"vultras.su/core/bond/urlenc"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Context struct {
|
type Context struct {
|
||||||
|
@ -61,15 +62,21 @@ func (c *Context) Close() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scan the incoming value from body depending
|
// Scan the incoming value from body depending
|
||||||
// on the content type of the request.
|
// on the content type of the request into the
|
||||||
|
// structure or map[string] any.
|
||||||
func (c *Context) Scan(v any) bool {
|
func (c *Context) Scan(v any) bool {
|
||||||
if c.dec == nil {
|
if c.dec == nil {
|
||||||
typ := c.ContentType()
|
typ := c.ContentType()
|
||||||
switch typ {
|
switch typ {
|
||||||
case contents.Json:
|
case contents.Json:
|
||||||
c.dec = json.NewDecoder(c.R.Body)
|
c.dec = json.NewDecoder(c.R.Body)
|
||||||
//case contents.UrlEncoded:
|
case contents.UrlEncoded:
|
||||||
// return false
|
body, _ := io.ReadAll(c.R.Body)
|
||||||
|
err := urlenc.Unmarshal(body, v)
|
||||||
|
if err != nil {
|
||||||
|
c.scanErr = err
|
||||||
|
}
|
||||||
|
return false
|
||||||
default:
|
default:
|
||||||
c.scanErr = UnknownContentTypeErr
|
c.scanErr = UnknownContentTypeErr
|
||||||
return false
|
return false
|
||||||
|
|
145
parse.go
145
parse.go
|
@ -1,147 +1,2 @@
|
||||||
package bond
|
package bond
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
"fmt"
|
|
||||||
"net/url"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ParseStr(encodedString string, result map[string]interface{}) error {
|
|
||||||
// build nested map.
|
|
||||||
var build func(map[string]interface{}, []string, interface{}) error
|
|
||||||
|
|
||||||
build = func(result map[string]interface{}, keys []string, value interface{}) error {
|
|
||||||
length := len(keys)
|
|
||||||
// trim ',"
|
|
||||||
key := strings.Trim(keys[0], "'\"")
|
|
||||||
if length == 1 {
|
|
||||||
result[key] = value
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// The end is slice. like f[], f[a][]
|
|
||||||
if keys[1] == "" && length == 2 {
|
|
||||||
// todo nested slice
|
|
||||||
if key == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
val, ok := result[key]
|
|
||||||
if !ok {
|
|
||||||
result[key] = []interface{}{value}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
children, ok := val.([]interface{})
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("expected type '[]interface{}' for key '%s', but got '%T'", key, val)
|
|
||||||
}
|
|
||||||
result[key] = append(children, value)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// The end is slice + map. like f[][a]
|
|
||||||
if keys[1] == "" && length > 2 && keys[2] != "" {
|
|
||||||
val, ok := result[key]
|
|
||||||
if !ok {
|
|
||||||
result[key] = []interface{}{}
|
|
||||||
val = result[key]
|
|
||||||
}
|
|
||||||
children, ok := val.([]interface{})
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("expected type '[]interface{}' for key '%s', but got '%T'", key, val)
|
|
||||||
}
|
|
||||||
if l := len(children); l > 0 {
|
|
||||||
if child, ok := children[l-1].(map[string]interface{}); ok {
|
|
||||||
if _, ok := child[keys[2]]; !ok {
|
|
||||||
_ = build(child, keys[2:], value)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
child := map[string]interface{}{}
|
|
||||||
_ = build(child, keys[2:], value)
|
|
||||||
result[key] = append(children, child)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// map. like f[a], f[a][b]
|
|
||||||
val, ok := result[key]
|
|
||||||
if !ok {
|
|
||||||
result[key] = map[string]interface{}{}
|
|
||||||
val = result[key]
|
|
||||||
}
|
|
||||||
children, ok := val.(map[string]interface{})
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("expected type 'map[string]interface{}' for key '%s', but got '%T'", key, val)
|
|
||||||
}
|
|
||||||
|
|
||||||
return build(children, keys[1:], value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// split encodedString.
|
|
||||||
parts := strings.Split(encodedString, "&")
|
|
||||||
for _, part := range parts {
|
|
||||||
pos := strings.Index(part, "=")
|
|
||||||
if pos <= 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
key, err := url.QueryUnescape(part[:pos])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for key[0] == ' ' {
|
|
||||||
key = key[1:]
|
|
||||||
}
|
|
||||||
if key == "" || key[0] == '[' {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
value, err := url.QueryUnescape(part[pos+1:])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// split into multiple keys
|
|
||||||
var keys []string
|
|
||||||
left := 0
|
|
||||||
for i, k := range key {
|
|
||||||
if k == '[' && left == 0 {
|
|
||||||
left = i
|
|
||||||
} else if k == ']' {
|
|
||||||
if left > 0 {
|
|
||||||
if len(keys) == 0 {
|
|
||||||
keys = append(keys, key[:left])
|
|
||||||
}
|
|
||||||
keys = append(keys, key[left+1:i])
|
|
||||||
left = 0
|
|
||||||
if i+1 < len(key) && key[i+1] != '[' {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(keys) == 0 {
|
|
||||||
keys = append(keys, key)
|
|
||||||
}
|
|
||||||
// first key
|
|
||||||
first := ""
|
|
||||||
for i, chr := range keys[0] {
|
|
||||||
if chr == ' ' || chr == '.' || chr == '[' {
|
|
||||||
first += "_"
|
|
||||||
} else {
|
|
||||||
first += string(chr)
|
|
||||||
}
|
|
||||||
if chr == '[' {
|
|
||||||
first += keys[0][i+1:]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
keys[0] = first
|
|
||||||
|
|
||||||
// build nested map
|
|
||||||
if err := build(result, keys, value); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
173
urlenc/scan.go
Normal file
173
urlenc/scan.go
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
package urlenc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
"encoding/json"
|
||||||
|
"strings"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Unmarshal(bts []byte, v any) error {
|
||||||
|
unesc, err := url.QueryUnescape(string(bts))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
mp := map[string] any{}
|
||||||
|
err = parseStr(unesc, mp)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
js, err := json.Marshal(mp)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(js, v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseStr(encodedString string, result map[string]any) error {
|
||||||
|
// build nested map.
|
||||||
|
var build func(map[string]any, []string, any) error
|
||||||
|
|
||||||
|
build = func(result map[string]any, keys []string, value any) error {
|
||||||
|
length := len(keys)
|
||||||
|
// trim ',"
|
||||||
|
key := strings.Trim(keys[0], "'\"")
|
||||||
|
if length == 1 {
|
||||||
|
result[key] = value
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// The end is slice. like f[], f[a][]
|
||||||
|
if keys[1] == "" && length == 2 {
|
||||||
|
// todo nested slice
|
||||||
|
if key == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
val, ok := result[key]
|
||||||
|
if !ok {
|
||||||
|
result[key] = []any{value}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
children, ok := val.([]any)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("expected type '[]any' for key '%s', but got '%T'", key, val)
|
||||||
|
}
|
||||||
|
result[key] = append(children, value)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// The end is slice + map. like f[][a]
|
||||||
|
if keys[1] == "" && length > 2 && keys[2] != "" {
|
||||||
|
val, ok := result[key]
|
||||||
|
if !ok {
|
||||||
|
result[key] = []any{}
|
||||||
|
val = result[key]
|
||||||
|
}
|
||||||
|
children, ok := val.([]any)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("expected type '[]any' for key '%s', but got '%T'", key, val)
|
||||||
|
}
|
||||||
|
if l := len(children); l > 0 {
|
||||||
|
if child, ok := children[l-1].(map[string]any); ok {
|
||||||
|
if _, ok := child[keys[2]]; !ok {
|
||||||
|
_ = build(child, keys[2:], value)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
child := map[string]any{}
|
||||||
|
_ = build(child, keys[2:], value)
|
||||||
|
result[key] = append(children, child)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// map. like f[a], f[a][b]
|
||||||
|
val, ok := result[key]
|
||||||
|
if !ok {
|
||||||
|
result[key] = map[string]any{}
|
||||||
|
val = result[key]
|
||||||
|
}
|
||||||
|
children, ok := val.(map[string]any)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("expected type 'map[string]any' for key '%s', but got '%T'", key, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
return build(children, keys[1:], value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// split encodedString.
|
||||||
|
parts := strings.Split(encodedString, "&")
|
||||||
|
for _, part := range parts {
|
||||||
|
pos := strings.Index(part, "=")
|
||||||
|
if pos <= 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
key, err := url.QueryUnescape(part[:pos])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for key[0] == ' ' {
|
||||||
|
key = key[1:]
|
||||||
|
}
|
||||||
|
if key == "" || key[0] == '[' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
value, err := url.QueryUnescape(part[pos+1:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// split into multiple keys
|
||||||
|
var keys []string
|
||||||
|
left := 0
|
||||||
|
for i, k := range key {
|
||||||
|
if k == '[' && left == 0 {
|
||||||
|
left = i
|
||||||
|
} else if k == ']' {
|
||||||
|
if left > 0 {
|
||||||
|
if len(keys) == 0 {
|
||||||
|
keys = append(keys, key[:left])
|
||||||
|
}
|
||||||
|
keys = append(keys, key[left+1:i])
|
||||||
|
left = 0
|
||||||
|
if i+1 < len(key) && key[i+1] != '[' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(keys) == 0 {
|
||||||
|
keys = append(keys, key)
|
||||||
|
}
|
||||||
|
// first key
|
||||||
|
first := ""
|
||||||
|
for i, chr := range keys[0] {
|
||||||
|
if chr == ' ' || chr == '.' || chr == '[' {
|
||||||
|
first += "_"
|
||||||
|
} else {
|
||||||
|
first += string(chr)
|
||||||
|
}
|
||||||
|
if chr == '[' {
|
||||||
|
first += keys[0][i+1:]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
keys[0] = first
|
||||||
|
|
||||||
|
// build nested map
|
||||||
|
if err := build(result, keys, value); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
Loading…
Reference in a new issue