diff --git a/README.md b/README.md index d3e4313..a91c5a7 100644 --- a/README.md +++ b/README.md @@ -135,7 +135,7 @@ verysimple -c server.json verysimple 继承 v2simple的一个优点,就是服务端的配置也可以用url做到。谁规定url只能用于分享客户端配置了?一条url肯定比json更容易配置,不容易出错。 -关于url格式,见: https://github.com/e1732a364fed/v2ray_simple/discussions/163 +关于url格式,见: [url标准定义](docs/url.md) ### 命令行模式 diff --git a/advLayer/grpc/grpc.go b/advLayer/grpc/grpc.go index ff37695..150b9f2 100644 --- a/advLayer/grpc/grpc.go +++ b/advLayer/grpc/grpc.go @@ -42,6 +42,7 @@ package grpc import ( "github.com/e1732a364fed/v2ray_simple/advLayer" + "github.com/e1732a364fed/v2ray_simple/utils" ) func init() { @@ -79,10 +80,10 @@ func (Creator) NewClientFromConf(conf *advLayer.Conf) (advLayer.Client, error) { grpc_multi := false if extra := conf.Extra; len(extra) > 0 { if thing := extra["grpc_multi"]; thing != nil { - if use_multi, ok := thing.(bool); ok { - grpc_multi = use_multi - } + if v, ok := utils.AnyToBool(thing); ok { + grpc_multi = v + } } } diff --git a/advLayer/quic/quic.go b/advLayer/quic/quic.go index 9f5ce8b..210586d 100644 --- a/advLayer/quic/quic.go +++ b/advLayer/quic/quic.go @@ -191,7 +191,7 @@ func getExtra(extra map[string]any) (useHysteria, hysteria_manual bool, maxStreamsInOneConn int64) { if thing := extra["maxStreamsInOneConn"]; thing != nil { - if count, ok := thing.(int64); ok && count > 0 { + if count, ok := utils.AnyToInt64(thing); ok && count > 0 { if ce := utils.CanLogInfo("Quic max Streams In One Conn"); ce != nil { ce.Write(zap.Int("count,", int(count))) } else { @@ -209,7 +209,7 @@ func getExtra(extra map[string]any) (useHysteria, hysteria_manual bool, useHysteria = true if thing := extra["mbps"]; thing != nil { - if mbps, ok := thing.(int64); ok && mbps > 1 { + if mbps, ok := utils.AnyToInt64(thing); ok && mbps > 1 { maxbyteCount = int(mbps) * 1024 * 1024 / 8 } } else { @@ -224,7 +224,7 @@ func getExtra(extra map[string]any) (useHysteria, hysteria_manual bool, } if thing := extra["hy_manual"]; thing != nil { - if ismanual, ok := thing.(bool); ok { + if ismanual, ok := utils.AnyToBool(thing); ok { hysteria_manual = ismanual if ismanual { @@ -235,7 +235,7 @@ func getExtra(extra map[string]any) (useHysteria, hysteria_manual bool, } if thing := extra["hy_manual_initial_rate"]; thing != nil { - if initRate, ok := thing.(float64); ok { + if initRate, ok := utils.AnyToFloat64(thing); ok { if rateOk(initRate) == 0 { TheCustomRate = initRate diff --git a/cmd/verysimple/cli.go b/cmd/verysimple/cli.go index f3a5bd0..ab31e22 100644 --- a/cmd/verysimple/cli.go +++ b/cmd/verysimple/cli.go @@ -372,8 +372,8 @@ func interactively_hotLoadUrlConfig() { Select := promptui.Select{ Label: "请选择你的url的格式类型", Items: []string{ - "协议官方url格式(视代理协议不同而不同)", "vs标准url格式", + "协议官方url格式(视代理协议不同而不同)", }, } i, result, err := Select.Run() @@ -386,16 +386,16 @@ func interactively_hotLoadUrlConfig() { fmt.Printf("你选择了 %s\n", result) switch i { - case 0: + case 1: fmt.Printf("目前暂不支持") return - case 1: + case 0: Select := promptui.Select{ - Label: "请选择该url是用于服务端还是客户端", + Label: "请选择该url是用于dial还是listen", Items: []string{ - "客户端", - "服务端", + "dial", + "listen", }, } i, result, err := Select.Run() @@ -418,7 +418,7 @@ func interactively_hotLoadUrlConfig() { } if i == 0 { - u, sn, _, okTls, err := proxy.GetRealProtocolFromClientUrl(theUrlStr) + u, sn, creator, okTls, err := proxy.GetRealProtocolFromClientUrl(theUrlStr) if err != nil { fmt.Printf("parse url failed %v\n", err) return @@ -432,12 +432,17 @@ func interactively_hotLoadUrlConfig() { fmt.Printf("parse url failed %v\n", err) return } + dc, err = creator.URLToDialConf(u, dc, proxy.UrlStandardFormat) + if err != nil { + fmt.Printf("parse url step 2 failed %v\n", err) + return + } hotLoadDialConfForRuntime("", []*proxy.DialConf{dc}) } else { - u, sn, _, okTls, err := proxy.GetRealProtocolFromServerUrl(theUrlStr) + u, sn, creator, okTls, err := proxy.GetRealProtocolFromServerUrl(theUrlStr) if err != nil { fmt.Printf("parse url failed %v\n", err) return @@ -453,7 +458,11 @@ func interactively_hotLoadUrlConfig() { fmt.Printf("parse url failed %v\n", err) return } - + lc, err = creator.URLToListenConf(u, lc, proxy.UrlStandardFormat) + if err != nil { + fmt.Printf("parse url step 2 failed %v\n", err) + return + } hotLoadListenConfForRuntime([]*proxy.ListenConf{lc}) } return diff --git a/configAdapter/configAdapter.go b/configAdapter/configAdapter.go index 982cf7c..936c0be 100644 --- a/configAdapter/configAdapter.go +++ b/configAdapter/configAdapter.go @@ -5,6 +5,8 @@ package configAdapter provides methods to convert proxy.ListenConf and proxy.Dia 参考 https://github.com/e1732a364fed/v2ray_simple/discussions/163 +以及 docs/url.md + 本包内的函数不支持 vs约定的 末尾+s即表示使用tls的用法, 所以调用函数之前要预处理一下。 本包依然秉持KISS原则,用最笨的代码、最少的依赖,实现最小的可执行文件大小以及最快的执行速度。 diff --git a/docs/url.md b/docs/url.md index 1695670..22159e5 100644 --- a/docs/url.md +++ b/docs/url.md @@ -1,8 +1,6 @@ # vs的通用url格式 ( verysimple Standard URL Format ) -虽然verysimple通用url格式从一开始就存在,但是却没有文档详细描述(阅读proxy/creator_url.go 可以了解,毕竟golang的理念是代码就是文档。)。那么在这里就作为文档进行介绍。 - vs的通用url格式,并不遵循现存市面上的其他格式,而是针对vs的架构逻辑所设计的新格式。如果你是一个vs老手,则使用vs的通用url格式你会感觉得心应手。 注意,要想读懂本文档,需要了解url的基础知识。 @@ -20,8 +18,19 @@ authority = [userinfo "@"] host [":" port] ``` -举例: -`vlesss://a684455c-b14f-11ea-bf0d-42010aaa0003@127.0.0.1:4433?insecure=true&v=0&utls=true#myvless1` +## 举例 + +``` +vlesss://a684455c-b14f-11ea-bf0d-42010aaa0003@127.0.0.1:4433?insecure=true&v=0&utls=true#my_vless1 + +vlesss://a684455c-b14f-11ea-bf0d-42010aaa0003@127.0.0.1:4433?adv=quic&v=0&extra.maxStreamsInOneConn=6&extra.congestion_control=hy&extra.mbps=1024#my_vless_quic + +dokodemo://?target.ip=1.1.1.1&target.port=80#my_doko + +shadowsocks://aes-128-gcm:mypasswordxxxxx@127.0.0.1:8000#my_ss + +socks5://myusername:mypassword@127.0.0.1:1080#my_socks5 +``` ## 基础部分 @@ -37,7 +46,7 @@ authority = [userinfo "@"] host [":" port] 熟悉ssh的同学会看出差别,ssh中,userinfo 是不包含密码的,也就是说ssh 需要 额外参数 或 步骤 来输入密码 或 等价信息 才能进行鉴权,而我们这里直接将鉴权的信息也包含在userinfo中来。 -在vless/vmess中 就是 uuid,在socks5/http中 就是 “用户名+密码”,在ss/trojan中就是 密码。 +在vless/vmess中 就是 uuid, 在socks5/http中, 使用 “用户名:密码”的形式, 在shadowsocks中, 使用 method:password 的形式, 在trojan 中就是 密码。 ### host:port @@ -51,7 +60,7 @@ authority = [userinfo "@"] host [":" port] ## query -v=0, 控制该proxy协议的 版本。 +v, 控制该proxy协议的 版本。 fallback=:80 设置回落的地址。 @@ -59,11 +68,46 @@ network 设置 使用的传输层,如 network="udp", 如不给出,则默认n fullcone=true设置 是否需要udp的fullcone功能。 -security=aes-128-gcm 设置 vmess/ss等存在多种加密方式等proxy的 具体加密方式 +security=aes-128-gcm 设置 vmess/ss等存在多种加密方式等proxy的 具体加密方式 adv=ws 设置使用的高级层,如不给出则没有高级层,如给出,可选 ws, grpc, quic -mux=true 是否使用内层smux +path=/mypath 设置 http头存在时,或者用 ws的 路径 或 grpc的 service name。 + +### http 头相关 + +http=true 设置是否使用http头 + +http.method=GET 设置 http的method + +http.version=1.1 设置http的版本 + +以 "header." 开头的字段:开头字母大写后,放到Header里。 + +比如,如果 vmess://uuid?http=true&header.host=myhost.com&header.Accept-Encoding=gzip,deflate + +则会设置http头的header 的 Host 字段为 myhost.com,设置 Accept-Encoding 为 gzip,deflate, 如果有空格等情况的话,需要进行 百分号转译。 + +转译可以使用这个工具测试: https://www.url-encode-decode.com/ + +### extra 相关 + +在标准toml配置中,有时会填充extra项来配置额外参数。在本标准url格式中,该类配置也是相当简单的。 + +就是用 extra.yyy=zzz 的格式 + +举几个例子,比如 reject 类型的proxy 需要一个 type 的extra参数 来指示 使用什么类型的拒绝响应; +此时在url中,这么写: extra.type=nginx + +再举几个例子 + +``` +#quic +extra.maxStreamsInOneConn=6&extra.congestion_control=hy&extra.mbps=1024 + +#grpc (非grpcSimple的情况) +extra.grpc_multi=true +``` ### tls相关 (proxy/tlsConfig.go) @@ -75,8 +119,8 @@ cert=cert.pem&key=cert.key 用于设置证书名称。 - - - +### 其他 + +dokodemo的目标这么写: `target.ip=1.1.1.1&target.port=80` diff --git a/proxy/config_url.go b/proxy/config_url.go index d04c5d8..2d9f23e 100644 --- a/proxy/config_url.go +++ b/proxy/config_url.go @@ -19,7 +19,7 @@ const ( var ( // Url格式 设置以何种方式解析 命令行模式/极简模式 中出现的url配置 // - //关于url格式的详细, 见 https://github.com/e1732a364fed/v2ray_simple/discussions/163 + //关于url格式的详细, 见 docs/url.md UrlFormat = UrlStandardFormat ) @@ -175,6 +175,8 @@ func URLToCommonConf(u *url.URL, conf *CommonConf) error { conf.Network = q.Get("network") + conf.Uuid = u.User.Username() + conf.Fullcone = utils.QueryPositive(q, "fullcone") conf.Tag = u.Fragment conf.Path = u.Path @@ -281,5 +283,19 @@ func URLToListenConf(u *url.URL, conf *ListenConf) error { conf.TLSKey = keyFile } + targetStr := q.Get("target.ip") + + if targetStr != "" { + target_portStr := q.Get("target.port") + if target_portStr != "" { + if conf.Network == "" { + conf.Network = "tcp" + } + taStr := conf.Network + "://" + targetStr + ":" + target_portStr + conf.TargetAddr = taStr + } + + } + return e } diff --git a/proxy/dokodemo/server.go b/proxy/dokodemo/server.go index 95f31b6..3ba4ff7 100644 --- a/proxy/dokodemo/server.go +++ b/proxy/dokodemo/server.go @@ -53,7 +53,6 @@ func init() { type ServerCreator struct{} -// 用如下参数形式: network=tcp&target=127.0.0.1&target_port=80 func (ServerCreator) URLToListenConf(url *url.URL, lc *proxy.ListenConf, format int) (*proxy.ListenConf, error) { if format != proxy.StandardMode { return lc, utils.ErrUnImplemented @@ -62,20 +61,6 @@ func (ServerCreator) URLToListenConf(url *url.URL, lc *proxy.ListenConf, format return nil, utils.ErrNilParameter } - targetStr := url.Query().Get("target") - if targetStr == "" { - return nil, utils.ErrNilParameter - } - - target_portStr := url.Query().Get("target_port") - if target_portStr == "" { - return nil, utils.ErrNilParameter - } - if lc.Network == "" { - lc.Network = "tcp" - } - taStr := lc.Network + "://" + targetStr + ":" + target_portStr - lc.TargetAddr = taStr return lc, nil } diff --git a/proxy/http/server.go b/proxy/http/server.go index 776f9d7..f83e85d 100644 --- a/proxy/http/server.go +++ b/proxy/http/server.go @@ -57,13 +57,16 @@ func (ServerCreator) URLToListenConf(u *url.URL, lc *proxy.ListenConf, format in lc = &proxy.ListenConf{} } - user := u.Query().Get("user") - pass := u.Query().Get("pass") + if p, set := u.User.Password(); set { + + user := u.User.Username() + pass := p + lc.Users = append(lc.Users, utils.UserConf{ + User: user, + Pass: pass, + }) + } - lc.Users = append(lc.Users, utils.UserConf{ - User: user, - Pass: pass, - }) return lc, nil } diff --git a/proxy/reject.go b/proxy/reject.go index ffa0ad1..de7ff16 100644 --- a/proxy/reject.go +++ b/proxy/reject.go @@ -66,29 +66,11 @@ func (RejectCreator) NewClient(dc *DialConf) (Client, error) { return r, nil } -func (RejectCreator) initCommonConfByURL(url *url.URL, lc *CommonConf, format int) { - - if format != UrlStandardFormat { - return - } - - nStr := url.Query().Get("type") - - if nStr != "" { - - lc.Extra = make(map[string]any) - lc.Extra["type"] = nStr - - } - -} - func (rc RejectCreator) URLToDialConf(url *url.URL, iv *DialConf, format int) (*DialConf, error) { if iv == nil { iv = &DialConf{} } - rc.initCommonConfByURL(url, &iv.CommonConf, format) return iv, nil } @@ -97,7 +79,6 @@ func (rc RejectCreator) URLToListenConf(url *url.URL, iv *ListenConf, format int if iv == nil { iv = &ListenConf{} } - rc.initCommonConfByURL(url, &iv.CommonConf, format) return iv, nil } diff --git a/proxy/shadowsocks/client.go b/proxy/shadowsocks/client.go index d2d15a1..c88158e 100644 --- a/proxy/shadowsocks/client.go +++ b/proxy/shadowsocks/client.go @@ -25,10 +25,11 @@ func (ClientCreator) URLToDialConf(u *url.URL, dc *proxy.DialConf, format int) ( dc = &proxy.DialConf{} } - m := u.Query().Get("method") - p := u.Query().Get("pass") + if p, set := u.User.Password(); set { + + dc.Uuid = "method:" + u.User.Username() + "\npass:" + p + } - dc.Uuid = "method:" + m + "\npass:" + p return dc, nil } diff --git a/proxy/shadowsocks/server.go b/proxy/shadowsocks/server.go index fbbd4b0..d926fdf 100644 --- a/proxy/shadowsocks/server.go +++ b/proxy/shadowsocks/server.go @@ -38,10 +38,10 @@ func (ServerCreator) URLToListenConf(u *url.URL, lc *proxy.ListenConf, format in lc = &proxy.ListenConf{} } - m := u.Query().Get("method") - p := u.Query().Get("pass") + if p, set := u.User.Password(); set { + lc.Uuid = "method:" + u.User.Username() + "\npass:" + p + } - lc.Uuid = "method:" + m + "\npass:" + p return lc, nil } diff --git a/proxy/socks5/client.go b/proxy/socks5/client.go index 8304013..6abd570 100644 --- a/proxy/socks5/client.go +++ b/proxy/socks5/client.go @@ -25,10 +25,10 @@ func (ClientCreator) URLToDialConf(u *url.URL, dc *proxy.DialConf, format int) ( dc = &proxy.DialConf{} } - user := u.Query().Get("user") - pass := u.Query().Get("pass") + if p, set := u.User.Password(); set { + dc.Uuid = "user:" + u.User.Username() + "\npass:" + p + } - dc.Uuid = "user:" + user + "\npass:" + pass return dc, nil } diff --git a/proxy/socks5/server.go b/proxy/socks5/server.go index 8e85e80..a611419 100644 --- a/proxy/socks5/server.go +++ b/proxy/socks5/server.go @@ -50,13 +50,15 @@ func (ServerCreator) URLToListenConf(u *url.URL, lc *proxy.ListenConf, format in lc = &proxy.ListenConf{} } - user := u.Query().Get("user") - pass := u.Query().Get("pass") + if p, set := u.User.Password(); set { + user := u.User.Username() + pass := p + lc.Users = append(lc.Users, utils.UserConf{ + User: user, + Pass: pass, + }) + } - lc.Users = append(lc.Users, utils.UserConf{ - User: user, - Pass: pass, - }) return lc, nil } diff --git a/proxy/trojan/client.go b/proxy/trojan/client.go index 2e142ea..bd54133 100644 --- a/proxy/trojan/client.go +++ b/proxy/trojan/client.go @@ -25,9 +25,10 @@ func (ClientCreator) URLToDialConf(url *url.URL, dc *proxy.DialConf, format int) case proxy.UrlStandardFormat: if dc == nil { dc = &proxy.DialConf{} + uuidStr := url.User.Username() + dc.Uuid = uuidStr + } - uuidStr := url.User.Username() - dc.Uuid = uuidStr return dc, nil default: return nil, utils.ErrUnImplemented diff --git a/proxy/trojan/server.go b/proxy/trojan/server.go index 00ac561..42cc4e6 100644 --- a/proxy/trojan/server.go +++ b/proxy/trojan/server.go @@ -35,11 +35,10 @@ func (ServerCreator) URLToListenConf(url *url.URL, lc *proxy.ListenConf, format case proxy.UrlStandardFormat: if lc == nil { lc = &proxy.ListenConf{} - + uuidStr := url.User.Username() + lc.Uuid = uuidStr } - uuidStr := url.User.Username() - lc.Uuid = uuidStr return lc, nil default: return nil, utils.ErrUnImplemented diff --git a/proxy/vless/client.go b/proxy/vless/client.go index 45dea86..f633a59 100644 --- a/proxy/vless/client.go +++ b/proxy/vless/client.go @@ -59,27 +59,8 @@ func (ClientCreator) URLToDialConf(url *url.URL, dc *proxy.DialConf, format int) case proxy.UrlStandardFormat: if dc == nil { dc = &proxy.DialConf{} - } - uuidStr := url.User.Username() - dc.Uuid = uuidStr - - vStr := url.Query().Get("v") - if vStr != "" { - v, e := strconv.Atoi(vStr) - if e != nil { - return nil, e - } - dc.Version = v - if v == 1 { - vless1_udp_multiStr := url.Query().Get("vless1_udp_multi") - if vless1_udp_multiStr == "true" || vless1_udp_multiStr == "1" { - if dc.Extra == nil { - dc.Extra = make(map[string]any) - } - dc.Extra["vless1_udp_multi"] = true - - } - } + uuidStr := url.User.Username() + dc.Uuid = uuidStr } diff --git a/proxy/vless/server.go b/proxy/vless/server.go index ad0cd23..d710051 100644 --- a/proxy/vless/server.go +++ b/proxy/vless/server.go @@ -6,7 +6,6 @@ import ( "io" "net" "net/url" - "strconv" "unsafe" "github.com/e1732a364fed/v2ray_simple/netLayer" @@ -54,18 +53,8 @@ func (ServerCreator) URLToListenConf(url *url.URL, lc *proxy.ListenConf, format if lc == nil { lc = &proxy.ListenConf{} - } - - uuidStr := url.User.Username() - lc.Uuid = uuidStr - vStr := url.Query().Get("v") - if vStr != "" { - v, e := strconv.Atoi(vStr) - if e != nil { - return nil, e - } - lc.Version = v - + uuidStr := url.User.Username() + lc.Uuid = uuidStr } return lc, nil diff --git a/proxy/vmess/client.go b/proxy/vmess/client.go index 4371165..2bdf8b4 100644 --- a/proxy/vmess/client.go +++ b/proxy/vmess/client.go @@ -56,23 +56,12 @@ func (ClientCreator) URLToDialConf(url *url.URL, dc *proxy.DialConf, format int) if dc == nil { dc = &proxy.DialConf{} - } + uuidStr := url.User.Username() - uuidStr := url.User.Username() - - dc.Uuid = uuidStr - - query := url.Query() - - security := query.Get("security") - if security != "" { - if dc.Extra == nil { - dc.Extra = make(map[string]any) - } - - dc.Extra[Security_confStr] = security + dc.Uuid = uuidStr } + return dc, nil } diff --git a/proxy/vmess/server.go b/proxy/vmess/server.go index b61d151..f74ab1b 100644 --- a/proxy/vmess/server.go +++ b/proxy/vmess/server.go @@ -76,11 +76,10 @@ func (ServerCreator) URLToListenConf(url *url.URL, lc *proxy.ListenConf, format if lc == nil { lc = &proxy.ListenConf{} + uuidStr := url.User.Username() + lc.Uuid = uuidStr } - uuidStr := url.User.Username() - lc.Uuid = uuidStr - return lc, nil default: return lc, utils.ErrUnImplemented diff --git a/utils/cast.go b/utils/cast.go new file mode 100644 index 0000000..632acec --- /dev/null +++ b/utils/cast.go @@ -0,0 +1,133 @@ +package utils + +import ( + "net/url" + "strconv" + "strings" +) + +func StrPositive(value string) bool { + value = strings.ToLower(value) + return value == "1" || value == "on" || value == "true" +} +func StrNegative(value string) bool { + value = strings.ToLower(value) + + return value == "" || value == "0" || value == "off" || value == "false" +} + +func QueryPositive(query url.Values, key string) bool { + nStr := query.Get(key) + return StrPositive(nStr) +} + +func QueryNegative(query url.Values, key string) bool { + nStr := query.Get(key) + return StrNegative(nStr) +} + +func AnyToBool(a any) (v bool, ok bool) { + ok = true + switch value := a.(type) { + case bool: + v = value + case int: + if value == 0 { + v = false + } else { + v = true + } + case int64: + if value == 0 { + v = false + } else { + v = true + } + case string: + if StrNegative(value) { + v = false + } else if StrPositive(value) { + v = true + } + default: + var i64 int64 + i64, ok = AnyToInt64(a) + if ok { + v, ok = AnyToBool(i64) + return + } + ok = false + } + + return +} + +func AnyToInt64(a any) (int64, bool) { + switch value := a.(type) { + case int64: + return value, true + case int: + return int64(value), true + case uint: + return int64(value), true + case int32: + return int64(value), true + case int16: + return int64(value), true + case int8: + return int64(value), true + case uint64: + return int64(value), true + case uint32: + return int64(value), true + case uint16: + return int64(value), true + case uint8: + return int64(value), true + case float64: + return int64(value), true + case float32: + return int64(value), true + case string: + v, err := strconv.Atoi(value) + if err == nil { + return int64(v), true + } + } + return 0, false +} + +func AnyToFloat64(a any) (float64, bool) { + switch value := a.(type) { + case float64: + return value, true + case float32: + return float64(value), true + + case int64: + return float64(value), true + case int: + return float64(value), true + case uint: + return float64(value), true + case int32: + return float64(value), true + case int16: + return float64(value), true + case int8: + return float64(value), true + case uint32: + return float64(value), true + case uint16: + return float64(value), true + case uint8: + return float64(value), true + case string: + v, err := strconv.ParseFloat(value, 64) + if err == nil { + return v, true + } + + } + return 0, false +} diff --git a/utils/utils.go b/utils/utils.go index 0ffc9d8..eda70b6 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -3,7 +3,6 @@ package utils import ( "flag" - "net/url" "os" "strings" @@ -86,13 +85,3 @@ func WrapFuncForPromptUI(f func(string) bool) func(string) error { return ErrInvalidData } } - -func QueryPositive(query url.Values, key string) bool { - nStr := query.Get(key) - return nStr == "true" || nStr == "1" -} - -func QueryNegative(query url.Values, key string) bool { - nStr := query.Get(key) - return nStr == "false" || nStr == "0" -} diff --git a/utils/uuid.go b/utils/uuid.go index 0a86266..0d25df6 100644 --- a/utils/uuid.go +++ b/utils/uuid.go @@ -67,7 +67,7 @@ GenerateUUID_v4 生成符合v4标准的uuid. version: https://datatracker.ietf.org/doc/html/rfc4122#section-4.1.3 */ -func GenerateUUID_v4() (r [UUID_BytesLen]byte) { +func GenerateUUID_v4() (r UUID) { rand.Reader.Read(r[:]) r[6] = (r[6] & 0x0f) | 0x40 // Version 4 r[8] = (r[8] & 0x3f) | 0x80