first
This commit is contained in:
parent
7fa952faf5
commit
2340fd4fa4
|
@ -1,23 +1,2 @@
|
|||
# ---> Go
|
||||
# If you prefer the allow list template instead of the deny list, see community template:
|
||||
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
||||
#
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
|
||||
# Go workspace file
|
||||
go.work
|
||||
|
||||
.*
|
||||
/go.sum
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
package crypt
|
||||
|
||||
import "hash"
|
||||
|
||||
type Crypt interface {
|
||||
Hash(data ...[]byte) []byte
|
||||
NewHash() hash.Hash
|
||||
Encrypt(data []byte, key []byte, iv []byte) (enData []byte, err error)
|
||||
Decrypt(enData []byte, key []byte, iv []byte) (data []byte, err error)
|
||||
GenKey() (priKey []byte, pubKey []byte, err error)
|
||||
Sign(data []byte, priKey []byte) (signature []byte, err error)
|
||||
Verify(data []byte, signature []byte, pubKey []byte) bool
|
||||
EncryptE(data []byte, pubKey []byte) (enData []byte, err error)
|
||||
DecryptE(enData []byte, priKey []byte) (data []byte, err error)
|
||||
}
|
||||
|
||||
type CMCrypt struct {
|
||||
}
|
||||
|
||||
type GMCrypt struct {
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package crypt
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/ZZMarquis/gm/sm4"
|
||||
"github.com/ZZMarquis/gm/util"
|
||||
"github.com/ssgo/u"
|
||||
)
|
||||
|
||||
func (c *CMCrypt) Encrypt(data []byte, key []byte, iv []byte) (enData []byte, err error) {
|
||||
return u.EncryptAesBytes(data, key, iv)
|
||||
}
|
||||
|
||||
func (c *CMCrypt) Decrypt(enData []byte, key []byte, iv []byte) (data []byte, err error) {
|
||||
return u.DecryptAesBytes(enData, key, iv)
|
||||
}
|
||||
|
||||
func (c *GMCrypt) Encrypt(data []byte, key []byte, iv []byte) (enData []byte, err error) {
|
||||
return sm4.CBCEncrypt(key[0:16], iv[0:16], util.PKCS5Padding(data, 16))
|
||||
}
|
||||
|
||||
func (c *GMCrypt) Decrypt(enData []byte, key []byte, iv []byte) (data []byte, err error) {
|
||||
data, err = sm4.CBCDecrypt(key[0:16], iv[0:16], enData)
|
||||
if err == nil {
|
||||
length := len(data)
|
||||
if length > 0 {
|
||||
unpadding := int(data[length-1])
|
||||
if length-unpadding >= 0 {
|
||||
data = util.PKCS5UnPadding(data)
|
||||
} else {
|
||||
return nil, errors.New("failed to decrypt by gm4")
|
||||
}
|
||||
} else {
|
||||
return nil, errors.New("failed to decrypt by gm4")
|
||||
}
|
||||
}
|
||||
return data, err
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package crypt
|
||||
|
||||
import (
|
||||
"crypto/sha512"
|
||||
"github.com/ZZMarquis/gm/sm3"
|
||||
"hash"
|
||||
)
|
||||
|
||||
func (c *CMCrypt) NewHash() hash.Hash {
|
||||
return sha512.New()
|
||||
}
|
||||
|
||||
func (c *GMCrypt) NewHash() hash.Hash {
|
||||
return sm3.New()
|
||||
}
|
||||
|
||||
func (c *CMCrypt) Hash(data ...[]byte) []byte {
|
||||
return makeHash(c.NewHash(), data...)
|
||||
}
|
||||
|
||||
func (c *GMCrypt) Hash(data ...[]byte) []byte {
|
||||
return makeHash(c.NewHash(), data...)
|
||||
}
|
||||
|
||||
func makeHash(h hash.Hash, data ...[]byte) []byte {
|
||||
for _, b := range data {
|
||||
h.Write(b)
|
||||
}
|
||||
return h.Sum(nil)
|
||||
}
|
|
@ -0,0 +1,163 @@
|
|||
package crypt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"github.com/emmansun/gmsm/sm2"
|
||||
"github.com/obscuren/ecies"
|
||||
"github.com/ssgo/u"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
func makePriKey(priKey []byte, curve elliptic.Curve) *ecdsa.PrivateKey {
|
||||
x, y := curve.ScalarBaseMult(priKey)
|
||||
return &ecdsa.PrivateKey{
|
||||
D: new(big.Int).SetBytes(priKey),
|
||||
PublicKey: ecdsa.PublicKey{
|
||||
Curve: curve,
|
||||
X: x,
|
||||
Y: y,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func makePubKey(pubKey []byte, curve elliptic.Curve) *ecdsa.PublicKey {
|
||||
keyLen := pubKey[0]
|
||||
x := new(big.Int)
|
||||
y := new(big.Int)
|
||||
x.SetBytes(pubKey[1 : keyLen+1])
|
||||
y.SetBytes(pubKey[keyLen+1:])
|
||||
return &ecdsa.PublicKey{Curve: curve, X: x, Y: y}
|
||||
}
|
||||
|
||||
func (c *CMCrypt) GenKey() (priKey []byte, pubKey []byte, err error) {
|
||||
pri, err := ecdsa.GenerateKey(elliptic.P256(), u.GlobalRand2)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
buf.WriteByte(byte(len(pri.X.Bytes())))
|
||||
buf.Write(pri.X.Bytes())
|
||||
buf.Write(pri.Y.Bytes())
|
||||
return pri.D.Bytes(), buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (c *CMCrypt) Sign(data []byte, priKey []byte) (signature []byte, err error) {
|
||||
r, s, err := ecdsa.Sign(u.GlobalRand1, makePriKey(priKey, elliptic.P256()), u.Sha256(data))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
buf.WriteByte(byte(len(r.Bytes())))
|
||||
buf.Write(r.Bytes())
|
||||
buf.Write(s.Bytes())
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (c *CMCrypt) Verify(data []byte, signature []byte, pubKey []byte) bool {
|
||||
byteLen := signature[0]
|
||||
r := new(big.Int)
|
||||
s := new(big.Int)
|
||||
r.SetBytes(signature[1 : byteLen+1])
|
||||
s.SetBytes(signature[byteLen+1:])
|
||||
return ecdsa.Verify(makePubKey(pubKey, elliptic.P256()), u.Sha256(data), r, s)
|
||||
}
|
||||
|
||||
func (c *CMCrypt) EncryptE(data []byte, pubKey []byte) (enData []byte, err error) {
|
||||
pub := ecies.ImportECDSAPublic(makePubKey(pubKey, elliptic.P256()))
|
||||
if r, err := ecies.Encrypt(u.GlobalRand1, pub, data, nil, nil); err == nil {
|
||||
return r, nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CMCrypt) DecryptE(enData []byte, priKey []byte) (data []byte, err error) {
|
||||
pri := ecies.ImportECDSA(makePriKey(priKey, elliptic.P256()))
|
||||
if r, err := pri.Decrypt(u.GlobalRand1, enData, nil, nil); err == nil {
|
||||
return r, nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// SM2
|
||||
|
||||
func (c *GMCrypt) GenKey() (priKey []byte, pubKey []byte, err error) {
|
||||
pri, err := sm2.GenerateKey(u.GlobalRand2)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
buf.WriteByte(byte(len(pri.X.Bytes())))
|
||||
buf.Write(pri.X.Bytes())
|
||||
buf.Write(pri.Y.Bytes())
|
||||
return pri.D.Bytes(), buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (c *GMCrypt) Sign(data []byte, priKey []byte) (signature []byte, err error) {
|
||||
r, s, err := sm2.SignWithSM2(u.GlobalRand1, makePriKey(priKey, sm2.P256()), nil, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
buf.WriteByte(byte(len(r.Bytes())))
|
||||
buf.Write(r.Bytes())
|
||||
buf.Write(s.Bytes())
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (c *GMCrypt) Verify(data []byte, signature []byte, pubKey []byte) bool {
|
||||
byteLen := signature[0]
|
||||
r := new(big.Int)
|
||||
s := new(big.Int)
|
||||
r.SetBytes(signature[1 : byteLen+1])
|
||||
s.SetBytes(signature[byteLen+1:])
|
||||
return sm2.VerifyWithSM2(makePubKey(pubKey, sm2.P256()), nil, data, r, s)
|
||||
}
|
||||
|
||||
func (c *GMCrypt) EncryptE(data []byte, pubKey []byte) (enData []byte, err error) {
|
||||
if r, err := sm2.Encrypt(u.GlobalRand1, makePubKey(pubKey, sm2.P256()), data, nil); err == nil {
|
||||
return r, nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (c *GMCrypt) DecryptE(enData []byte, priKey []byte) (data []byte, err error) {
|
||||
if r, err := sm2.Decrypt(&sm2.PrivateKey{PrivateKey: *makePriKey(priKey, sm2.P256())}, enData); err == nil {
|
||||
return r, nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
//func (c *GMCrypt) GenKey() (priKey []byte, pubKey []byte, err error) {
|
||||
// pri, pub, err := sm2.GenerateKey(u.GlobalRand2)
|
||||
// if err != nil {
|
||||
// return nil, nil, err
|
||||
// }
|
||||
// return pri.GetRawBytes(), pub.GetRawBytes(), nil
|
||||
//}
|
||||
//
|
||||
//func (c *GMCrypt) Sign(data []byte, priKey []byte) (signature []byte, err error) {
|
||||
// pri, err := sm2.RawBytesToPrivateKey(priKey)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// signature, err = sm2.Sign(pri, nil, u.Sha256(data))
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// return signature, nil
|
||||
//}
|
||||
//
|
||||
//func (c *GMCrypt) Verify(data []byte, signature []byte, pubKey []byte) bool {
|
||||
// pub, err := sm2.RawBytesToPublicKey(pubKey)
|
||||
// if err != nil {
|
||||
// return false
|
||||
// }
|
||||
// return sm2.Verify(pub, nil, u.Sha256(data), signature)
|
||||
//}
|
|
@ -0,0 +1,426 @@
|
|||
package crypto
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"github.com/ZZMarquis/gm/sm3"
|
||||
"github.com/api-go/plugin"
|
||||
"github.com/api-go/plugins/crypto/crypt"
|
||||
"hash"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var gmCrypto = &crypt.GMCrypt{}
|
||||
var cmCrypto = &crypt.CMCrypt{}
|
||||
var defaultCrypto crypt.Crypt
|
||||
var defaultCryptoLock = sync.RWMutex{}
|
||||
|
||||
func init() {
|
||||
plugin.Register(plugin.Plugin{
|
||||
Id: "crypto",
|
||||
Name: "加密算法",
|
||||
ConfigSample: `mode: cm # 指定默认算法集,cm-通用算法(SHA256、AES、ECDSA),gm-国密算法(sm3、sm4、sm2)`,
|
||||
Init: func(conf map[string]interface{}) {
|
||||
defaultCryptoLock.Lock()
|
||||
if conf["mode"] == "gm" {
|
||||
defaultCrypto = &crypt.GMCrypt{}
|
||||
} else {
|
||||
defaultCrypto = &crypt.CMCrypt{}
|
||||
}
|
||||
defaultCryptoLock.Unlock()
|
||||
}, Objects: map[string]interface{}{
|
||||
// base64 Base64编码
|
||||
"base64": map[string]interface{}{
|
||||
// encode 将字符串编码
|
||||
// encode data 用于编码的数据,string类型
|
||||
// encode return 编码后的结果
|
||||
"encode": func(data string) string {
|
||||
return base64.StdEncoding.EncodeToString([]byte(data))
|
||||
},
|
||||
// encodeBytes 将二进制数据编码
|
||||
// encodeBytes data 用于编码的数据,二进制的字节数组
|
||||
// encodeBytes return 编码后的结果
|
||||
"encodeBytes": func(data []byte) string {
|
||||
return base64.StdEncoding.EncodeToString(data)
|
||||
},
|
||||
// decode 解码为字符串,如果发生错误将抛出异常
|
||||
// decode data 编码后的结果
|
||||
// decode return 解码后的数据,string类型
|
||||
"decode": func(data string) (string, error) {
|
||||
return makeStringResult(base64.StdEncoding.DecodeString(data))
|
||||
},
|
||||
// decodeBytes 解码为二进制数据,如果发生错误将抛出异常
|
||||
// decodeBytes data 编码后的结果
|
||||
// decodeBytes return 解码后的数据,二进制的字节数组
|
||||
"decodeBytes": func(data string) ([]byte, error) {
|
||||
return base64.StdEncoding.DecodeString(data)
|
||||
},
|
||||
},
|
||||
// urlBase64 Base64编码(兼容URL)
|
||||
"urlBase64": map[string]interface{}{
|
||||
"encode": func(data string) string {
|
||||
return base64.URLEncoding.EncodeToString([]byte(data))
|
||||
},
|
||||
"encodeBytes": func(data []byte) string {
|
||||
return base64.URLEncoding.EncodeToString(data)
|
||||
},
|
||||
"decode": func(data string) (string, error) {
|
||||
return makeStringResult(base64.URLEncoding.DecodeString(data))
|
||||
},
|
||||
"decodeBytes": func(data string) ([]byte, error) {
|
||||
return base64.URLEncoding.DecodeString(data)
|
||||
},
|
||||
},
|
||||
// hex hex编码
|
||||
"hex": map[string]interface{}{
|
||||
"encode": func(data string) string {
|
||||
return hex.EncodeToString([]byte(data))
|
||||
},
|
||||
"encodeBytes": func(data []byte) string {
|
||||
return hex.EncodeToString(data)
|
||||
},
|
||||
"decode": func(data string) (string, error) {
|
||||
return makeStringResult(hex.DecodeString(data))
|
||||
},
|
||||
"decodeBytes": func(data string) ([]byte, error) {
|
||||
return hex.DecodeString(data)
|
||||
},
|
||||
},
|
||||
// newMD5 创建MD5对象
|
||||
// newMD5 return MD5对象
|
||||
"newMD5": func() *Hash {
|
||||
return &Hash{hash: md5.New()}
|
||||
},
|
||||
// md5 将字符串进行MD5编码
|
||||
// md5 data 用于编码的数据,string类型
|
||||
// md5 return 编码后的结果,16进制字符串
|
||||
"md5": func(data string) string {
|
||||
return hex.EncodeToString(makeHash(md5.New(), []byte(data)))
|
||||
},
|
||||
// md5 将二进制数据进行MD5编码
|
||||
// md5 data 用于编码的数据,用于编码的数据,二进制的字节数组
|
||||
// md5 return 编码后的结果,二进制的字节数组,如果需要转换为字符串可以使用hex.encode或者base64.encode等编码工具进行编码
|
||||
"md5Bytes": func(data []byte) []byte {
|
||||
return makeHash(md5.New(), data)
|
||||
},
|
||||
|
||||
// newSHA1 创建SHA1对象
|
||||
// newSHA1 return SHA1对象
|
||||
"newSHA1": func() *Hash {
|
||||
return &Hash{hash: sha1.New()}
|
||||
},
|
||||
// sha1 将字符串进行SHA1编码
|
||||
// sha1 data 用于编码的数据,string类型
|
||||
// sha1 return 编码后的结果,16进制字符串
|
||||
"sha1": func(data string) string {
|
||||
return hex.EncodeToString(makeHash(sha1.New(), []byte(data)))
|
||||
},
|
||||
// sha1Bytes 将二进制数据进行SHA1编码
|
||||
// sha1Bytes data 用于编码的数据,用于编码的数据,二进制的字节数组
|
||||
// sha1Bytes return 编码后的结果,二进制的字节数组,如果需要转换为字符串可以使用hex.encode或者base64.encode等编码工具进行编码
|
||||
"sha1Bytes": func(data []byte) []byte {
|
||||
return makeHash(sha1.New(), data)
|
||||
},
|
||||
// newSHA256 创建SHA256对象
|
||||
// newSHA256 return SHA256对象
|
||||
"newSHA256": func() *Hash {
|
||||
return &Hash{hash: sha256.New()}
|
||||
},
|
||||
// sha256 将字符串进行SHA256编码
|
||||
// sha256 data 用于编码的数据,string类型
|
||||
// sha256 return 编码后的结果,16进制字符串
|
||||
"sha256": func(data string) string {
|
||||
return hex.EncodeToString(makeHash(sha256.New(), []byte(data)))
|
||||
},
|
||||
// sha256Bytes 将二进制数据进行SHA256编码
|
||||
// sha256Bytes data 用于编码的数据,用于编码的数据,二进制的字节数组
|
||||
// sha256Bytes return 编码后的结果,二进制的字节数组,如果需要转换为字符串可以使用hex.encode或者base64.encode等编码工具进行编码
|
||||
"sha256Bytes": func(data []byte) []byte {
|
||||
return makeHash(sha256.New(), data)
|
||||
},
|
||||
// newSHA512 创建SHA512对象
|
||||
// newSHA512 return SHA512对象
|
||||
"newSHA512": func() *Hash {
|
||||
return &Hash{hash: sha512.New()}
|
||||
},
|
||||
// sha512 将字符串进行SHA512编码
|
||||
// sha512 data 用于编码的数据,string类型
|
||||
// sha512 return 编码后的结果,16进制字符串
|
||||
"sha512": func(data string) string {
|
||||
return hex.EncodeToString(makeHash(sha512.New(), []byte(data)))
|
||||
},
|
||||
// sha512Bytes 将二进制数据进行SHA512编码
|
||||
// sha512Bytes data 用于编码的数据,用于编码的数据,二进制的字节数组
|
||||
// sha512Bytes return 编码后的结果,二进制的字节数组,如果需要转换为字符串可以使用hex.encode或者base64.encode等编码工具进行编码
|
||||
"sha512Bytes": func(data []byte) []byte {
|
||||
return makeHash(sha512.New(), data)
|
||||
},
|
||||
// newSM3 创建SM3对象
|
||||
// newSM3 return SM3对象
|
||||
"newSM3": func() *Hash {
|
||||
return &Hash{hash: sm3.New()}
|
||||
},
|
||||
// sm3 将字符串进行SM3编码
|
||||
// sm3 data 用于编码的数据,string类型
|
||||
// sm3 return 编码后的结果,16进制字符串
|
||||
"sm3": func(data string) string {
|
||||
return hex.EncodeToString(makeHash(sm3.New(), []byte(data)))
|
||||
},
|
||||
// sm3Bytes 将二进制数据进行SM3编码
|
||||
// sm3Bytes data 用于编码的数据,用于编码的数据,二进制的字节数组
|
||||
// sm3Bytes return 编码后的结果,二进制的字节数组,如果需要转换为字符串可以使用hex.encode或者base64.encode等编码工具进行编码
|
||||
"sm3Bytes": func(data []byte) []byte {
|
||||
return makeHash(sm3.New(), data)
|
||||
},
|
||||
// newHash 创建Hash对象(根据配置使用SHA256或SM3,默认为SHA256)
|
||||
// newHash return Hash对象
|
||||
"newHash": func() *Hash {
|
||||
return &Hash{hash: defaultCrypto.NewHash()}
|
||||
},
|
||||
// hash 将字符串进行Hash编码(根据配置使用SHA256或SM3,默认为SHA256)
|
||||
// hash data 用于编码的数据,string类型
|
||||
// hash return 编码后的结果,16进制字符串
|
||||
"hash": func(data string) string {
|
||||
return hex.EncodeToString(makeHash(defaultCrypto.NewHash(), []byte(data)))
|
||||
},
|
||||
// hashBytes 将二进制数据进行Hash编码(根据配置使用SHA256或SM3,默认为SHA256)
|
||||
// hashBytes data 用于编码的数据,用于编码的数据,二进制的字节数组
|
||||
// hashBytes return 编码后的结果,二进制的字节数组,如果需要转换为字符串可以使用hex.encode或者base64.encode等编码工具进行编码
|
||||
"hashBytes": func(data []byte) []byte {
|
||||
return makeHash(defaultCrypto.NewHash(), data)
|
||||
},
|
||||
// aes 使用AES算法(CBC)进行加解密
|
||||
"aes": &Aes{crypto: cmCrypto},
|
||||
// sm4 使用SM4算法进行加解密
|
||||
"sm4": &Aes{crypto: gmCrypto},
|
||||
// symmetric 根据配置使用使用AES或SM4算法进行加解密(默认为AES)
|
||||
"symmetric": &Aes{crypto: defaultCrypto},
|
||||
// ecdsa 使用ECDSA算法进行签名或加解密
|
||||
"ecdsa": &Ecdsa{crypto: cmCrypto},
|
||||
// sm2 使用SM2算法进行签名或加解密
|
||||
"sm2": &Ecdsa{crypto: gmCrypto},
|
||||
// asymmetric 根据配置使用使用ECDSA或SM2算法进行加解密(默认为ECDSA)
|
||||
"asymmetric": &Ecdsa{crypto: defaultCrypto},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
type Hash struct {
|
||||
hash hash.Hash
|
||||
}
|
||||
|
||||
// Add 添加数据到Hash
|
||||
// Add data 字符串格式的数据
|
||||
// Add return Hash对象,方便串联操作
|
||||
func (hash *Hash) Add(data string) *Hash {
|
||||
hash.hash.Write([]byte(data))
|
||||
return hash
|
||||
}
|
||||
|
||||
// AddHex 添加Hex编码的数据到Hash
|
||||
// AddHex data Hex编码的二进制数据
|
||||
// AddHex return Hash对象,方便串联操作
|
||||
func (hash *Hash) AddHex(data string) *Hash {
|
||||
hash.hash.Write([]byte(data))
|
||||
return hash
|
||||
}
|
||||
|
||||
// AddBytes 添加二进制数据到Hash
|
||||
// AddBytes data 二进制数据
|
||||
// AddBytes return Hash对象,方便串联操作
|
||||
func (hash *Hash) AddBytes(p []byte) *Hash {
|
||||
hash.hash.Write(p)
|
||||
return hash
|
||||
}
|
||||
|
||||
// Make 生成Hash
|
||||
// Make return Hex编码的结果
|
||||
func (hash *Hash) Make() string {
|
||||
return hex.EncodeToString(hash.MakeBytes())
|
||||
}
|
||||
|
||||
// MakeBytes 生成Hash
|
||||
// MakeBytes return 二进制结果
|
||||
func (hash *Hash) MakeBytes() []byte {
|
||||
return hash.hash.Sum(nil)
|
||||
}
|
||||
|
||||
type Aes struct {
|
||||
crypto crypt.Crypt
|
||||
}
|
||||
|
||||
// Encrypt 将字符串加密
|
||||
// Encrypt data 用于加密的数据,string类型
|
||||
// Encrypt key hex编码的16位或32位密钥
|
||||
// Encrypt iv hex编码的16位或32位向量
|
||||
// Encrypt return 返回hex编码的加密结果
|
||||
func (ae *Aes) Encrypt(data, key, iv string) (string, error) {
|
||||
if keyD, err1 := hex.DecodeString(key); err1 == nil {
|
||||
if ivD, err2 := hex.DecodeString(iv); err2 == nil {
|
||||
return makeHexStringResult(ae.crypto.Encrypt([]byte(data), keyD, ivD))
|
||||
} else {
|
||||
return "", err2
|
||||
}
|
||||
} else {
|
||||
return "", err1
|
||||
}
|
||||
}
|
||||
|
||||
// EncryptBytes 将二进制数据加密
|
||||
// EncryptBytes data 用于加密的数据,二进制的字节数组
|
||||
// EncryptBytes key 二进制的16位或32位密钥
|
||||
// EncryptBytes iv 二进制的16位或32位向量
|
||||
// EncryptBytes return 返回二进制的加密结果
|
||||
func (ae *Aes) EncryptBytes(data, key, iv []byte) ([]byte, error) {
|
||||
return ae.crypto.Encrypt(data, key, iv)
|
||||
}
|
||||
|
||||
// Decrypt 解密为字符串
|
||||
// Decrypt data hex编码的加密后结果
|
||||
// Decrypt key hex编码的16位或32位密钥
|
||||
// Decrypt iv hex编码的16位或32位向量
|
||||
// Decrypt return 解密后的数据,string类型
|
||||
func (ae *Aes) Decrypt(data string, key, iv string) (string, error) {
|
||||
if keyD, err1 := hex.DecodeString(key); err1 == nil {
|
||||
if ivD, err2 := hex.DecodeString(iv); err2 == nil {
|
||||
if dataD, err3 := hex.DecodeString(data); err3 == nil {
|
||||
return makeStringResult(ae.crypto.Decrypt(dataD, keyD, ivD))
|
||||
} else {
|
||||
return "", err3
|
||||
}
|
||||
} else {
|
||||
return "", err2
|
||||
}
|
||||
} else {
|
||||
return "", err1
|
||||
}
|
||||
}
|
||||
|
||||
// DecryptBytes 解密为二进制数据
|
||||
// DecryptBytes data 二进制的加密后结果
|
||||
// DecryptBytes key 二进制的16位或32位密钥
|
||||
// DecryptBytes iv 二进制的16位或32位向量
|
||||
// DecryptBytes return 解密后的数据,二进制的字节数组
|
||||
func (ae *Aes) DecryptBytes(data, key, iv []byte) ([]byte, error) {
|
||||
return ae.crypto.Decrypt(data, key, iv)
|
||||
}
|
||||
|
||||
type Ecdsa struct {
|
||||
crypto crypt.Crypt
|
||||
}
|
||||
|
||||
// GenKey 生成hex编码的公钥私钥
|
||||
// GenKey priKey hex编码的私钥信息
|
||||
// GenKey pubKey hex编码的公钥信息
|
||||
func (ec *Ecdsa) GenKey() (priKey string, pubKey string, error error) {
|
||||
buf1, buf2, err := ec.crypto.GenKey()
|
||||
return hex.EncodeToString(buf1), hex.EncodeToString(buf2), err
|
||||
}
|
||||
|
||||
// GenKeyBytes 生成二进制的公钥私钥
|
||||
// GenKeyBytes priKey 二进制的私钥信息
|
||||
// GenKeyBytes pubKey 二进制的公钥信息
|
||||
func (ec *Ecdsa) GenKeyBytes() (priKey []byte, pubKey []byte, error error) {
|
||||
return ec.crypto.GenKey()
|
||||
}
|
||||
|
||||
// Sign 对数据进行签名,返回hex编码格式的数据
|
||||
// Sign data 将要签名的数据
|
||||
// Sign priKey hex编码的私钥信息
|
||||
func (ec *Ecdsa) Sign(data string, priKey string) (string, error) {
|
||||
if priKeyD, err2 := hex.DecodeString(priKey); err2 == nil {
|
||||
return makeHexStringResult(ec.crypto.Sign([]byte(data), priKeyD))
|
||||
} else {
|
||||
return "", err2
|
||||
}
|
||||
}
|
||||
|
||||
// SignBytes 对数据进行签名,返回二进制的数据
|
||||
// SignBytes data 将要签名的二进制数据
|
||||
// SignBytes priKey 二进制的私钥信息
|
||||
func (ec *Ecdsa) SignBytes(data, priKey []byte) ([]byte, error) {
|
||||
return ec.crypto.Sign(data, priKey)
|
||||
}
|
||||
|
||||
// Verify 校验签名
|
||||
// Verify data 将要签名的数据
|
||||
// Verify signature hex编码的签名信息
|
||||
// Verify pubKey hex编码的公钥信息
|
||||
// Verify return 校验结果
|
||||
func (ec *Ecdsa) Verify(data string, signature string, pubKey string) (bool, error) {
|
||||
if signatureD, err1 := hex.DecodeString(signature); err1 == nil {
|
||||
if pubKeyD, err2 := hex.DecodeString(pubKey); err2 == nil {
|
||||
return ec.crypto.Verify([]byte(data), signatureD, pubKeyD), nil
|
||||
} else {
|
||||
return false, err2
|
||||
}
|
||||
} else {
|
||||
return false, err1
|
||||
}
|
||||
}
|
||||
|
||||
// VerifyBytes 校验签名
|
||||
// VerifyBytes data 将要签名的二进制数据
|
||||
// VerifyBytes signature 二进制的签名信息
|
||||
// VerifyBytes pubKey 二进制的公钥信息
|
||||
// VerifyBytes return 校验结果
|
||||
func (ec *Ecdsa) VerifyBytes(data, signature, pubKey []byte) bool {
|
||||
return ec.crypto.Verify(data, signature, pubKey)
|
||||
}
|
||||
|
||||
// Encrypt pubKey hex编码的公钥信息
|
||||
func (ec *Ecdsa) Encrypt(data, pubKey string) (string, error) {
|
||||
if pubKeyD, err := hex.DecodeString(pubKey); err == nil {
|
||||
return makeHexStringResult(ec.crypto.EncryptE([]byte(data), pubKeyD))
|
||||
} else {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
// EncryptBytes pubKey 二进制的公钥信息
|
||||
func (ec *Ecdsa) EncryptBytes(data, pubKey []byte) ([]byte, error) {
|
||||
return ec.crypto.EncryptE(data, pubKey)
|
||||
}
|
||||
|
||||
// Decrypt priKey hex编码的私钥信息
|
||||
func (ec *Ecdsa) Decrypt(data string, priKey string) (string, error) {
|
||||
if dataD, err1 := hex.DecodeString(data); err1 == nil {
|
||||
if priKeyD, err2 := hex.DecodeString(priKey); err2 == nil {
|
||||
return makeStringResult(ec.crypto.DecryptE(dataD, priKeyD))
|
||||
} else {
|
||||
return "", err2
|
||||
}
|
||||
} else {
|
||||
return "", err1
|
||||
}
|
||||
}
|
||||
|
||||
// DecryptBytes priKey 二进制的私钥信息
|
||||
func (ec *Ecdsa) DecryptBytes(data, priKey []byte) ([]byte, error) {
|
||||
return ec.crypto.DecryptE(data, priKey)
|
||||
}
|
||||
|
||||
func makeHash(h hash.Hash, data []byte) []byte {
|
||||
h.Write(data)
|
||||
return h.Sum(nil)
|
||||
}
|
||||
|
||||
func makeStringResult(buf []byte, err error) (string, error) {
|
||||
if err == nil {
|
||||
return string(buf), nil
|
||||
} else {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
func makeHexStringResult(buf []byte, err error) (string, error) {
|
||||
if err == nil {
|
||||
return hex.EncodeToString(buf), nil
|
||||
} else {
|
||||
return "", err
|
||||
}
|
||||
}
|
|
@ -0,0 +1,302 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/api-go/plugin"
|
||||
"github.com/ssgo/db"
|
||||
"github.com/ssgo/log"
|
||||
"github.com/ssgo/u"
|
||||
)
|
||||
|
||||
type DB struct {
|
||||
pool *db.DB
|
||||
}
|
||||
|
||||
type Tx struct {
|
||||
conn *db.Tx
|
||||
}
|
||||
|
||||
var dbPool = map[string]*db.DB{}
|
||||
var defaultDB *db.DB
|
||||
|
||||
func init() {
|
||||
plugin.Register(plugin.Plugin{
|
||||
Id: "db",
|
||||
Name: "数据库操作",
|
||||
ConfigSample: `default: mysql://root:<**encrypted_password**>@127.0.0.1:3306/1?maxIdles=0&maxLifeTime=0&maxOpens=0&logSlow=1s # set default db connection pool, used by db.xxx
|
||||
configs:
|
||||
conn1: sqlite3://conn1.db # set a named connection pool, used by db.get('conn1').xxx
|
||||
conn2: mysql://root:@127.0.0.1:3306/1?sslCa=<**encrypted**>&sslCert=<**encrypted**>&sslKey=<**encrypted**>&sslSkipVerify=true # set ssl connection pool for mysql
|
||||
conn3: mysql://root:@127.0.0.1:3306/1?timeout=90s&readTimeout=5s&writeTimeout=3s&charset=utf8mb4,utf8 # set more option for mysql
|
||||
`,
|
||||
|
||||
Init: func(conf map[string]interface{}) {
|
||||
if conf["default"] != nil {
|
||||
defaultDB = db.GetDB(u.String(conf["default"]), nil)
|
||||
}
|
||||
if conf["configs"] != nil {
|
||||
confs := map[string]string{}
|
||||
u.Convert(conf["configs"], &confs)
|
||||
for name, url := range confs {
|
||||
dbPool[name] = db.GetDB(url, nil)
|
||||
}
|
||||
}
|
||||
},
|
||||
Objects: map[string]interface{}{
|
||||
"fetch": GetDB,
|
||||
},
|
||||
// 实现直接使用db.xxx操作默认的数据库
|
||||
JsCode: `_db = db
|
||||
db = _db.fetch()
|
||||
db.fetch = _db.fetch
|
||||
`,
|
||||
})
|
||||
}
|
||||
|
||||
// GetDB 获得数据库连接
|
||||
// GetDB name 连接配置名称,如果不提供名称则使用默认连接
|
||||
// GetDB return 数据库连接,对象内置连接池操作,完成后无需手动关闭连接
|
||||
func GetDB(name *string, logger *log.Logger) *DB {
|
||||
if name == nil || *name == "" {
|
||||
if defaultDB != nil {
|
||||
return &DB{
|
||||
pool: defaultDB.CopyByLogger(logger),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if dbPool[*name] != nil {
|
||||
return &DB{
|
||||
pool: dbPool[*name].CopyByLogger(logger),
|
||||
}
|
||||
}
|
||||
}
|
||||
return &DB{
|
||||
pool: db.GetDB("", logger),
|
||||
}
|
||||
}
|
||||
|
||||
// Begin 开始事务
|
||||
// Begin return 事务对象,事务中的操作都在事务对象上操作,请务必在返回的事务对象上执行commit或rollback
|
||||
func (db *DB) Begin() *Tx {
|
||||
return db.Begin()
|
||||
}
|
||||
|
||||
// Exec 执行SQL
|
||||
// * requestSql SQL语句
|
||||
// * args SQL语句中问号变量的值,按顺序放在请求参数中
|
||||
// Exec return 如果是INSERT到含有自增字段的表中返回插入的自增ID,否则返回影响的行数
|
||||
func (db *DB) Exec(requestSql string, args ...interface{}) (int64, error) {
|
||||
r := db.pool.Exec(requestSql, args...)
|
||||
out := r.Id()
|
||||
if out == 0 {
|
||||
out = r.Changes()
|
||||
}
|
||||
return out, r.Error
|
||||
}
|
||||
|
||||
// Query 查询
|
||||
// Query return 返回查询到的数据,对象数组格式
|
||||
func (db *DB) Query(requestSql string, args ...interface{}) ([]map[string]interface{}, error) {
|
||||
r := db.pool.Query(requestSql, args...)
|
||||
return r.MapResults(), r.Error
|
||||
}
|
||||
|
||||
// Query1 查询
|
||||
// Query1 return 返回查询到的第一行数据,对象格式
|
||||
func (db *DB) Query1(requestSql string, args ...interface{}) (map[string]interface{}, error) {
|
||||
r := db.pool.Query(requestSql, args...)
|
||||
results := r.MapResults()
|
||||
if len(results) > 0 {
|
||||
return results[0], r.Error
|
||||
} else {
|
||||
return map[string]interface{}{}, r.Error
|
||||
}
|
||||
}
|
||||
|
||||
// Query11 查询
|
||||
// Query11 return 返回查询到的第一行第一列数据,字段类型对应的格式
|
||||
func (db *DB) Query11(requestSql string, args ...interface{}) (interface{}, error) {
|
||||
r := db.pool.Query(requestSql, args...)
|
||||
results := r.SliceResults()
|
||||
if len(results) > 0 {
|
||||
if len(results[0]) > 0 {
|
||||
return results[0][0], r.Error
|
||||
} else {
|
||||
return nil, r.Error
|
||||
}
|
||||
} else {
|
||||
return nil, r.Error
|
||||
}
|
||||
}
|
||||
|
||||
// Query1a 查询
|
||||
// Query1a return 返回查询到的第一列数据,数组格式
|
||||
func (db *DB) Query1a(requestSql string, args ...interface{}) ([]interface{}, error) {
|
||||
r := db.pool.Query(requestSql, args...)
|
||||
results := r.SliceResults()
|
||||
a := make([]interface{}, 0)
|
||||
for _, row := range results {
|
||||
if len(results[0]) > 0 {
|
||||
a = append(a, row[0])
|
||||
}
|
||||
}
|
||||
return a, r.Error
|
||||
}
|
||||
|
||||
// Insert 插入数据
|
||||
// * table 表名
|
||||
// * data 数据对象(Key-Value格式)
|
||||
// Insert return 如果是INSERT到含有自增字段的表中返回插入的自增ID,否则返回影响的行数
|
||||
func (db *DB) Insert(table string, data map[string]interface{}) (int64, error) {
|
||||
r := db.pool.Insert(table, data)
|
||||
out := r.Id()
|
||||
if out == 0 {
|
||||
out = r.Changes()
|
||||
}
|
||||
return out, r.Error
|
||||
}
|
||||
|
||||
// Replace 替换数据
|
||||
// Replace return 如果是REPLACE到含有自增字段的表中返回插入的自增ID,否则返回影响的行数
|
||||
func (db *DB) Replace(table string, data map[string]interface{}) (int64, error) {
|
||||
r := db.pool.Replace(table, data)
|
||||
out := r.Id()
|
||||
if out == 0 {
|
||||
out = r.Changes()
|
||||
}
|
||||
return out, r.Error
|
||||
}
|
||||
|
||||
// Update 更新数据
|
||||
// * wheres 条件(SQL中WHERE后面的部分)
|
||||
// Update return 返回影响的行数
|
||||
func (db *DB) Update(table string, data map[string]interface{}, wheres string, args ...interface{}) (int64, error) {
|
||||
r := db.pool.Update(table, data, wheres, args...)
|
||||
return r.Changes(), r.Error
|
||||
}
|
||||
|
||||
// Delete 删除数据
|
||||
// Delete return 返回影响的行数
|
||||
func (db *DB) Delete(table string, wheres string, args ...interface{}) (int64, error) {
|
||||
r := db.pool.Delete(table, wheres, args...)
|
||||
return r.Changes(), r.Error
|
||||
}
|
||||
|
||||
// MakeId 生成指定字段不唯一的ID
|
||||
// MakeId idField ID字段
|
||||
// MakeId idSize ID长度
|
||||
// MakeId return 新的ID
|
||||
func (db *DB) MakeId(table string, idField string, idSize uint) (string, error) {
|
||||
var id string
|
||||
var err error
|
||||
for i:=0; i<100; i++ {
|
||||
if idSize > 20 {
|
||||
id = u.UniqueId()
|
||||
} else if idSize > 14 {
|
||||
id = u.UniqueId()[0:idSize]
|
||||
} else if idSize > 12 {
|
||||
id = u.ShortUniqueId()[0:idSize]
|
||||
} else if idSize > 10 {
|
||||
id = u.Id12()[0:idSize]
|
||||
} else if idSize > 8 {
|
||||
id = u.Id10()[0:idSize]
|
||||
} else if idSize >= 6 {
|
||||
id = u.Id8()[0:idSize]
|
||||
} else {
|
||||
id = u.Id6()
|
||||
}
|
||||
r := db.pool.Query(fmt.Sprintf("SELECT COUNT(*) FROM `%s` WHERE `%s`=?", table, idField), id)
|
||||
err = r.Error
|
||||
if r.IntOnR1C1() == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return id, err
|
||||
}
|
||||
|
||||
// Commit 提交事务
|
||||
func (tx *Tx) Commit() error {
|
||||
return tx.conn.Commit()
|
||||
}
|
||||
|
||||
// Rollback 回滚事务
|
||||
func (tx *Tx) Rollback() error {
|
||||
return tx.conn.Rollback()
|
||||
}
|
||||
|
||||
// Finish 根据传入的成功标识提交或回滚事务
|
||||
// Finish ok 事务是否执行成功
|
||||
func (tx *Tx) Finish(ok bool) error {
|
||||
return tx.conn.Finish(ok)
|
||||
}
|
||||
|
||||
// CheckFinished 检查事务是否已经提交或回滚,如果事务没有结束则执行回滚操作
|
||||
func (tx *Tx) CheckFinished() error {
|
||||
return tx.conn.CheckFinished()
|
||||
}
|
||||
|
||||
func (tx *Tx) Exec(requestSql string, args ...interface{}) (int64, error) {
|
||||
r := tx.conn.Exec(requestSql, args...)
|
||||
return r.Changes(), r.Error
|
||||
}
|
||||
|
||||
func (tx *Tx) Query(requestSql string, args ...interface{}) ([]map[string]interface{}, error) {
|
||||
r := tx.conn.Query(requestSql, args...)
|
||||
return r.MapResults(), r.Error
|
||||
}
|
||||
|
||||
func (tx *Tx) Query1(requestSql string, args ...interface{}) (map[string]interface{}, error) {
|
||||
r := tx.conn.Query(requestSql, args...)
|
||||
results := r.MapResults()
|
||||
if len(results) > 0 {
|
||||
return results[0], r.Error
|
||||
} else {
|
||||
return map[string]interface{}{}, r.Error
|
||||
}
|
||||
}
|
||||
|
||||
func (tx *Tx) Query11(requestSql string, args ...interface{}) (interface{}, error) {
|
||||
r := tx.conn.Query(requestSql, args...)
|
||||
results := r.SliceResults()
|
||||
if len(results) > 0 {
|
||||
if len(results[0]) > 0 {
|
||||
return results[0][0], r.Error
|
||||
} else {
|
||||
return nil, r.Error
|
||||
}
|
||||
} else {
|
||||
return nil, r.Error
|
||||
}
|
||||
}
|
||||
|
||||
func (tx *Tx) Query1a(requestSql string, args ...interface{}) ([]interface{}, error) {
|
||||
r := tx.conn.Query(requestSql, args...)
|
||||
results := r.SliceResults()
|
||||
a := make([]interface{}, 0)
|
||||
for _, row := range results {
|
||||
if len(results[0]) > 0 {
|
||||
a = append(a, row[0])
|
||||
}
|
||||
}
|
||||
return a, r.Error
|
||||
}
|
||||
|
||||
func (tx *Tx) Insert(table string, data map[string]interface{}) (int64, error) {
|
||||
r := tx.conn.Insert(table, data)
|
||||
return r.Id(), r.Error
|
||||
}
|
||||
|
||||
func (tx *Tx) Replace(table string, data map[string]interface{}) (int64, error) {
|
||||
r := tx.conn.Replace(table, data)
|
||||
return r.Id(), r.Error
|
||||
}
|
||||
|
||||
func (tx *Tx) Update(table string, data map[string]interface{}, wheres string, args ...interface{}) (int64, error) {
|
||||
r := tx.conn.Update(table, data, wheres, args...)
|
||||
return r.Changes(), r.Error
|
||||
}
|
||||
|
||||
func (tx *Tx) Delete(table string, wheres string, args ...interface{}) (int64, error) {
|
||||
r := tx.conn.Delete(table, wheres, args...)
|
||||
return r.Changes(), r.Error
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
package discover
|
||||
|
||||
import (
|
||||
"github.com/api-go/plugin"
|
||||
"github.com/ssgo/discover"
|
||||
"github.com/ssgo/httpclient"
|
||||
"github.com/ssgo/log"
|
||||
"github.com/ssgo/u"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type DiscoverApp struct {
|
||||
app string
|
||||
token string
|
||||
caller *discover.Caller
|
||||
logger *log.Logger
|
||||
globalHeaders map[string]string
|
||||
}
|
||||
|
||||
func init() {
|
||||
plugin.Register(plugin.Plugin{
|
||||
Id: "discover",
|
||||
Name: "服务发现",
|
||||
Objects: map[string]interface{}{
|
||||
"fetch": GetDiscoverApp,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// GetDiscoverApp 获得一个服务发现的客户端,如果在配置(server>calls)中指定了AccessToken、超时时间或者HTTP协议(如:iZg753bnsBxTOqHjaeEdt2szvov95eLq34G6jiHBoeE=:1:s:60s)会自动在获得的客户端中设置好
|
||||
// GetDiscoverApp app 需要访问的服务名称
|
||||
// GetDiscoverApp return 服务发现客户端对象,支持的方法:get、post、put、delete、head、setGlobalHeaders
|
||||
func GetDiscoverApp(app string, request *http.Request, logger *log.Logger) *DiscoverApp {
|
||||
return &DiscoverApp{
|
||||
app: app,
|
||||
logger: logger,
|
||||
caller: discover.NewCaller(request, logger),
|
||||
globalHeaders: map[string]string{},
|
||||
}
|
||||
}
|
||||
|
||||
// SetGlobalHeaders 设置固定的HTTP头部信息,在每个请求中都加入这些HTTP头
|
||||
// * SetGlobalHeaders 传入一个Key-Value对象的HTTP头信息
|
||||
func (dApp *DiscoverApp) SetGlobalHeaders(headers map[string]string) {
|
||||
dApp.globalHeaders = headers
|
||||
}
|
||||
|
||||
// Get 发送GET请求
|
||||
// * path /开头的请求路径,调用时会自动加上负载均衡到的目标节点的URL前缀发送HTTP请求
|
||||
// * headers 传入一个Key-Value对象的HTTP头信息,如果不指定头信息这个参数可以省略不传
|
||||
// * return 返回结果对象,如果返回值是JSON格式,将自动转化为对象否则将字符串放在.result中,如发生错误将抛出异常,返回的对象中还包括:headers、statusCode、statusMessage
|
||||
func (dApp *DiscoverApp) Get(path string, headers *map[string]string) (map[string]interface{}, error) {
|
||||
return makeResult(dApp.logger, dApp.caller.Get(dApp.app, fixHTTPPath(path), dApp.makeHeaderArray(headers)...))
|
||||
}
|
||||
|
||||
// Post 发送POST请求
|
||||
// * data 可以传入任意类型,如果不是字符串或二进制数组时会自动添加application/json头,数据将以json格式发送
|
||||
func (dApp *DiscoverApp) Post(path string, data *map[string]interface{}, headers *map[string]string) (map[string]interface{}, error) {
|
||||
return makeResult(dApp.logger, dApp.caller.Post(dApp.app, fixHTTPPath(path), data, dApp.makeHeaderArray(headers)...))
|
||||
}
|
||||
|
||||
// Put 发送PUT请求
|
||||
func (dApp *DiscoverApp) Put(path string, data *map[string]interface{}, headers *map[string]string) (map[string]interface{}, error) {
|
||||
return makeResult(dApp.logger, dApp.caller.Put(dApp.app, fixHTTPPath(path), data, dApp.makeHeaderArray(headers)...))
|
||||
}
|
||||
|
||||
// Delete 发送DELETE请求
|
||||
func (dApp *DiscoverApp) Delete(path string, data *map[string]interface{}, headers *map[string]string) (map[string]interface{}, error) {
|
||||
return makeResult(dApp.logger, dApp.caller.Delete(dApp.app, fixHTTPPath(path), data, dApp.makeHeaderArray(headers)...))
|
||||
}
|
||||
|
||||
// Head 发送HEAD请求
|
||||
func (dApp *DiscoverApp) Head(path string, headers *map[string]string) (map[string]interface{}, error) {
|
||||
return makeResult(dApp.logger, dApp.caller.Head(dApp.app, fixHTTPPath(path), dApp.makeHeaderArray(headers)...))
|
||||
}
|
||||
|
||||
func (dApp *DiscoverApp) makeHeaderArray(in *map[string]string) []string {
|
||||
out := make([]string, 0)
|
||||
if dApp.globalHeaders != nil {
|
||||
for k, v := range dApp.globalHeaders {
|
||||
out = append(out, k, v)
|
||||
}
|
||||
}
|
||||
if in != nil {
|
||||
for k, v := range *in {
|
||||
out = append(out, k, v)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func fixHTTPPath(path string) string {
|
||||
if !strings.HasPrefix(path, "/") {
|
||||
return "/" + path
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
|
||||
func makeResult(logger *log.Logger, result *httpclient.Result) (map[string]interface{}, error) {
|
||||
r := map[string]interface{}{}
|
||||
if result.Error != nil {
|
||||
logger.Error(result.Error.Error())
|
||||
return nil, result.Error
|
||||
}
|
||||
|
||||
if result.Response != nil {
|
||||
headers := map[string]string{}
|
||||
for k, v := range result.Response.Header {
|
||||
if len(v) == 1 {
|
||||
headers[k] = v[0]
|
||||
} else {
|
||||
headers[k] = strings.Join(v, " ")
|
||||
}
|
||||
}
|
||||
r["headers"] = headers
|
||||
r["statusCode"] = result.Response.StatusCode
|
||||
r["statusMessage"] = result.Response.Status
|
||||
|
||||
if strings.Contains(result.Response.Header.Get("Content-Type"), "application/json") {
|
||||
u.UnJson(result.String(), &r)
|
||||
} else {
|
||||
r["result"] = result.String()
|
||||
}
|
||||
}
|
||||
return r, nil
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
// +build !windows
|
||||
|
||||
package file
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func init() {
|
||||
lockFile = func(f *os.File) {
|
||||
_ = syscall.Flock(int(f.Fd()), syscall.LOCK_SH)
|
||||
}
|
||||
unlockFile = func(f *os.File) {
|
||||
_ = syscall.Flock(int(f.Fd()), syscall.LOCK_UN)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,590 @@
|
|||
package file
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"github.com/api-go/plugin"
|
||||
"github.com/ssgo/u"
|
||||
"gopkg.in/yaml.v3"
|
||||
"os"
|
||||
"path"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var _allowPaths = make([]string, 0)
|
||||
var _allowExtensions = make([]string, 0)
|
||||
var notAllowMessage = ""
|
||||
var fileConfigLock = sync.RWMutex{}
|
||||
|
||||
var lockFile = func(f *os.File) {}
|
||||
var unlockFile = func(f *os.File) {}
|
||||
|
||||
type File struct {
|
||||
name string
|
||||
fd *os.File
|
||||
}
|
||||
type FileInfo struct {
|
||||
Name string
|
||||
Mtime int64
|
||||
IsDir bool
|
||||
Size int64
|
||||
}
|
||||
|
||||
func init() {
|
||||
plugin.Register(plugin.Plugin{
|
||||
Id: "file",
|
||||
Name: "文件操作",
|
||||
ConfigSample: `allowPaths: # 允许操作的文件路径
|
||||
- /
|
||||
allowExtensions: # 允许操作的文件后缀,以.开头,例如 .json .txt .db
|
||||
- .json
|
||||
- .txt
|
||||
notAllowMessage: no access for file # 当文件路径或文件后缀不被允许时返回的错误信息
|
||||
`,
|
||||
Init: func(conf map[string]interface{}) {
|
||||
newAllowPaths := make([]string, 0)
|
||||
newAllowExtensions := make([]string, 0)
|
||||
newNotAllowMessage := "file not allow to access"
|
||||
if conf["allowPaths"] != nil {
|
||||
u.Convert(conf["allowPaths"], &newAllowPaths)
|
||||
}
|
||||
if conf["allowExtensions"] != nil {
|
||||
u.Convert(conf["allowExtensions"], &newAllowExtensions)
|
||||
}
|
||||
if conf["notAllowMessage"] != nil {
|
||||
newNotAllowMessage = u.String(conf["notAllowMessage"])
|
||||
}
|
||||
fileConfigLock.Lock()
|
||||
_allowPaths = newAllowPaths
|
||||
_allowExtensions = newAllowExtensions
|
||||
notAllowMessage = newNotAllowMessage
|
||||
fileConfigLock.Unlock()
|
||||
},
|
||||
Objects: map[string]interface{}{
|
||||
// list 列出目录下的文件
|
||||
// * dirname 目录名称
|
||||
// list sortBy 排序依据[name|mtime|size],默认使用名称排序
|
||||
// list limit 返回指定数量,默认返回全部
|
||||
// list return 文件列表{name:文件名,mtime:最后修改时间,size:文件尺寸}
|
||||
"list": func(dirname string, sortBy *string, limit *int) ([]FileInfo, error) {
|
||||
dirname = fixPath(dirname)
|
||||
if !checkDirAllow(dirname) {
|
||||
return nil, errors.New(getNotAllowMessage(dirname))
|
||||
}
|
||||
u.CheckPath(path.Join(dirname, "_"))
|
||||
|
||||
if d, err := os.Open(dirname); err == nil {
|
||||
out := make([]FileInfo, 0)
|
||||
if files, err := d.Readdir(-1); err == nil {
|
||||
for _, f := range files {
|
||||
if !strings.HasPrefix(f.Name(), ".") {
|
||||
out = append(out, FileInfo{
|
||||
Name: f.Name(),
|
||||
Mtime: f.ModTime().Unix(),
|
||||
IsDir: f.IsDir(),
|
||||
Size: f.Size(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
_ = d.Close()
|
||||
if sortBy != nil {
|
||||
sort.Slice(out, func(i, j int) bool {
|
||||
if *sortBy == "mtime" {
|
||||
return out[i].Mtime > out[j].Mtime
|
||||
} else if *sortBy == "size" {
|
||||
return out[i].Size > out[j].Size
|
||||
} else {
|
||||
return out[i].Name < out[j].Name
|
||||
}
|
||||
})
|
||||
}
|
||||
if limit != nil && *limit > 0 && *limit < len(out) {
|
||||
return out[0:*limit], nil
|
||||
}
|
||||
return out, nil
|
||||
} else {
|
||||
return []FileInfo{}, err
|
||||
}
|
||||
},
|
||||
// exists 判断文件是否存在
|
||||
// exists return 是否存在
|
||||
"exists": func(filename string) bool {
|
||||
fi, err := os.Stat(filename)
|
||||
return err == nil && fi != nil
|
||||
},
|
||||
// isDir 判断是否文件夹
|
||||
// isDir return 是否文件夹
|
||||
"isDir": func(filename string) (bool, error) {
|
||||
filename = fixPath(filename)
|
||||
if fi, err := getFileStat(filename); err == nil {
|
||||
return fi.IsDir(), nil
|
||||
} else {
|
||||
return false, err
|
||||
}
|
||||
},
|
||||
// getModTime 返回文件的修改时间(时间戳格式)
|
||||
// getModTime return 修改时间的时间戳
|
||||
"getModTime": func(filename string) (int64, error) {
|
||||
filename = fixPath(filename)
|
||||
if fi, err := getFileStat(filename); err == nil {
|
||||
return fi.ModTime().Unix(), nil
|
||||
} else {
|
||||
return 0, err
|
||||
}
|
||||
},
|
||||
// getModTimeStr 返回文件的修改时间(字符串格式)
|
||||
// getModTimeStr return 修改时间的字符串
|
||||
"getModTimeStr": func(filename string) (string, error) {
|
||||
filename = fixPath(filename)
|
||||
if fi, err := getFileStat(filename); err == nil {
|
||||
return fi.ModTime().Format("2006-01-02 15:04:05"), nil
|
||||
} else {
|
||||
return "", err
|
||||
}
|
||||
},
|
||||
// makeDir 创建文件夹
|
||||
"makeDir": func(dirname string) error {
|
||||
dirname = fixPath(dirname)
|
||||
if !checkDirAllow(dirname) {
|
||||
return errors.New(getNotAllowMessage(dirname))
|
||||
}
|
||||
return os.MkdirAll(dirname, 0700)
|
||||
},
|
||||
// read 读取一个文件
|
||||
// * filename 文件名
|
||||
// read return 文件内容,字符串格式
|
||||
"read": func(filename string) (string, error) {
|
||||
filename = fixPath(filename)
|
||||
if f, err := openFileForRead(filename); err == nil {
|
||||
defer f.Close()
|
||||
return f.ReadAll()
|
||||
} else {
|
||||
return "", err
|
||||
}
|
||||
},
|
||||
// readBytes 读取一个二进制文件
|
||||
// readBytes return 文件内容,二进制格式
|
||||
"readBytes": func(filename string) ([]byte, error) {
|
||||
filename = fixPath(filename)
|
||||
if f, err := openFileForRead(filename); err == nil {
|
||||
defer f.Close()
|
||||
return f.ReadAllBytes()
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
},
|
||||
// readFileLines 按行读取文件
|
||||
// readFileLines return 文件内容,返回字符串数组
|
||||
"readFileLines": func(filename string) ([]string, error) {
|
||||
filename = fixPath(filename)
|
||||
if f, err := openFileForRead(filename); err == nil {
|
||||
defer f.Close()
|
||||
return f.ReadLines()
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
},
|
||||
// write 写入一个文件
|
||||
// write content 文件内容,字符串格式
|
||||
// write return 写入的字节数
|
||||
"write": func(filename, content string) (int, error) {
|
||||
filename = fixPath(filename)
|
||||
if f, err := openFileForWrite(filename); err == nil {
|
||||
defer f.Close()
|
||||
return f.Write(content)
|
||||
} else {
|
||||
return 0, err
|
||||
}
|
||||
},
|
||||
// writeBytes 写入一个二进制文件
|
||||
// writeBytes content 文件内容,二进制格式
|
||||
// writeBytes return 写入的字节数
|
||||
"writeBytes": func(filename string, content []byte) (int, error) {
|
||||
filename = fixPath(filename)
|
||||
if f, err := openFileForWrite(filename); err == nil {
|
||||
defer f.Close()
|
||||
return f.WriteBytes(content)
|
||||
} else {
|
||||
return 0, err
|
||||
}
|
||||
},
|
||||
"openForRead": openFileForRead,
|
||||
"openForWrite": openFileForWrite,
|
||||
"openForAppend": openFileForAppend,
|
||||
"open": openFile,
|
||||
// remove 删除文件
|
||||
"remove": func(filename string) error {
|
||||
filename = fixPath(filename)
|
||||
fi, err := getFileStat(filename)
|
||||
if err == nil && fi.IsDir() {
|
||||
if !checkDirAllow(filename) {
|
||||
return errors.New(getNotAllowMessage(filename))
|
||||
}
|
||||
return os.RemoveAll(filename)
|
||||
} else {
|
||||
if !checkFileAllow(filename) {
|
||||
return errors.New(getNotAllowMessage(filename))
|
||||
}
|
||||
return os.Remove(filename)
|
||||
}
|
||||
},
|
||||
// rename 修改文件名
|
||||
// * fileOldName 旧文件
|
||||
// * fileNewName 新文件
|
||||
"rename": func(fileOldName, fileNewName string) error {
|
||||
fi, err := getFileStat(fileOldName)
|
||||
if err == nil && fi.IsDir() {
|
||||
if !checkDirAllow(fileOldName) {
|
||||
return errors.New(getNotAllowMessage(fileOldName))
|
||||
}
|
||||
if !checkDirAllow(fileNewName) {
|
||||
return errors.New(getNotAllowMessage(fileNewName))
|
||||
}
|
||||
} else {
|
||||
if !checkFileAllow(fileOldName) {
|
||||
return errors.New(getNotAllowMessage(fileOldName))
|
||||
}
|
||||
if !checkFileAllow(fileNewName) {
|
||||
return errors.New(getNotAllowMessage(fileNewName))
|
||||
}
|
||||
}
|
||||
return os.Rename(fileOldName, fileNewName)
|
||||
},
|
||||
// copy 复制文件
|
||||
"copy": func(fileOldName, fileNewName string) error {
|
||||
fileNewName = fixPath(fileNewName)
|
||||
newFI, _ := getFileStat(fileNewName)
|
||||
fileOldName = fixPath(fileOldName)
|
||||
fi, err := getFileStat(fileOldName)
|
||||
if err == nil && fi.IsDir() {
|
||||
if !checkDirAllow(fileOldName) {
|
||||
return errors.New(getNotAllowMessage(fileOldName))
|
||||
}
|
||||
if !checkDirAllow(fileNewName) {
|
||||
return errors.New(getNotAllowMessage(fileNewName))
|
||||
}
|
||||
//if strings.HasSuffix(fileNewName, "/") {
|
||||
// u.CheckPath(path.Join(fileNewName, "a.txt"))
|
||||
//}else{
|
||||
// u.CheckPath(fileNewName)
|
||||
//}
|
||||
//_, err = u.RunCommand("cp", "-rf", fileOldName, fileNewName)
|
||||
//return err
|
||||
return copyDir(fileNewName, fileOldName)
|
||||
} else {
|
||||
if !checkFileAllow(fileOldName) {
|
||||
return errors.New(getNotAllowMessage(fileOldName))
|
||||
}
|
||||
if !checkFileAllow(fileNewName) {
|
||||
return errors.New(getNotAllowMessage(fileNewName))
|
||||
}
|
||||
if newFI != nil && newFI.IsDir() {
|
||||
fileNewName = path.Join(fileNewName, path.Base(fileOldName))
|
||||
}
|
||||
return copyFile(fileNewName, fileOldName)
|
||||
}
|
||||
},
|
||||
// saveJson 将对象存储为JSON格式的文件
|
||||
// saveJson content 要存储的对象
|
||||
"saveJson": func(filename string, content interface{}) error {
|
||||
filename = fixPath(filename)
|
||||
if !checkFileAllow(filename) {
|
||||
return errors.New(getNotAllowMessage(filename))
|
||||
}
|
||||
return u.SaveJsonP(filename, content)
|
||||
},
|
||||
// saveYaml 将对象存储为YAML格式的文件
|
||||
// saveYaml content 要存储的对象
|
||||
"saveYaml": func(filename string, content interface{}) error {
|
||||
filename = fixPath(filename)
|
||||
if !checkFileAllow(filename) {
|
||||
return errors.New(getNotAllowMessage(filename))
|
||||
}
|
||||
return u.SaveYaml(filename, content)
|
||||
},
|
||||
// loadJson 读取JSON格式的文件并转化为对象
|
||||
// loadJson return 对象
|
||||
"loadJson": func(filename string) (interface{}, error) {
|
||||
filename = fixPath(filename)
|
||||
if !checkFileAllow(filename) {
|
||||
return nil, errors.New(getNotAllowMessage(filename))
|
||||
}
|
||||
var data interface{}
|
||||
err := u.LoadJson(filename, &data)
|
||||
return data, err
|
||||
},
|
||||
// loadYaml 读取YAML格式的文件并转化为对象
|
||||
// loadYaml return 对象
|
||||
"loadYaml": func(filename string) (interface{}, error) {
|
||||
filename = fixPath(filename)
|
||||
if !checkFileAllow(filename) {
|
||||
return nil, errors.New(getNotAllowMessage(filename))
|
||||
}
|
||||
var data interface{}
|
||||
buf, err := u.ReadFileBytes(filename)
|
||||
if err == nil {
|
||||
err = yaml.Unmarshal(buf, &data)
|
||||
}
|
||||
return data, err
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func copyDir(dst, src string) error {
|
||||
if d, err := os.Open(src); err == nil {
|
||||
defer d.Close()
|
||||
if files, err := d.Readdir(-1); err == nil {
|
||||
for _, f := range files {
|
||||
if f.IsDir() {
|
||||
if err2 := copyDir(path.Join(dst, f.Name()), path.Join(src, f.Name())); err2 != nil {
|
||||
return err2
|
||||
}
|
||||
} else {
|
||||
if err2 := copyFile(path.Join(dst, f.Name()), path.Join(src, f.Name())); err2 != nil {
|
||||
return err2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func copyFile(dst, src string) error {
|
||||
if f, err := openFileForRead(src); err == nil {
|
||||
defer f.Close()
|
||||
if f2, err2 := openFileForWrite(dst); err == nil {
|
||||
defer f2.Close()
|
||||
for {
|
||||
if buf, err3 := f.ReadBytes(10240); err3 != nil {
|
||||
break
|
||||
} else {
|
||||
_, _ = f2.WriteBytes(buf)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
return err2
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func fixPath(filename string) string {
|
||||
if runtime.GOOS == "windows" {
|
||||
return strings.ReplaceAll(filename, "/", "\\")
|
||||
}
|
||||
return filename
|
||||
}
|
||||
|
||||
func getFileStat(filename string) (os.FileInfo, error) {
|
||||
fi, err := os.Stat(filename)
|
||||
if err == nil && fi.IsDir() {
|
||||
if !checkDirAllow(filename) {
|
||||
return nil, errors.New(getNotAllowMessage(filename))
|
||||
}
|
||||
} else {
|
||||
if !checkFileAllow(filename) {
|
||||
return nil, errors.New(getNotAllowMessage(filename))
|
||||
}
|
||||
}
|
||||
return fi, err
|
||||
}
|
||||
|
||||
// openFileForRead 打开一个用于读取的文件,若不存在会抛出异常
|
||||
// openFileForRead return 文件对象,请务必在使用完成后关闭文件
|
||||
func openFileForRead(filename string) (*File, error) {
|
||||
return _openFile(filename, os.O_RDONLY, 0400)
|
||||
}
|
||||
|
||||
// openFileForWrite 打开一个用于写入的文件,若不存在会自动创建
|
||||
// openFileForWrite return 文件对象,请务必在使用完成后关闭文件
|
||||
func openFileForWrite(filename string) (*File, error) {
|
||||
return _openFile(filename, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600)
|
||||
}
|
||||
|
||||
// openFileForAppend 打开一个用于追加写入的文件,若不存在会自动创建
|
||||
// openFileForAppend return 文件对象,请务必在使用完成后关闭文件
|
||||
func openFileForAppend(filename string) (*File, error) {
|
||||
return _openFile(filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
|
||||
}
|
||||
|
||||
// openFile 打开一个用于追加写入的文件,若不存在会自动创建
|
||||
// openFile return 文件对象,请务必在使用完成后关闭文件
|
||||
func openFile(filename string) (*File, error) {
|
||||
return _openFile(filename, os.O_CREATE|os.O_RDWR|os.O_SYNC, 0600)
|
||||
}
|
||||
func _openFile(filename string, flag int, perm os.FileMode) (*File, error) {
|
||||
if !checkFileAllow(filename) {
|
||||
return nil, errors.New(getNotAllowMessage(filename))
|
||||
}
|
||||
u.CheckPath(filename)
|
||||
fd, err := os.OpenFile(filename, flag, perm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lockFile(fd)
|
||||
return &File{name: filename, fd: fd}, nil
|
||||
}
|
||||
|
||||
// Close 关闭文件
|
||||
func (f *File) Close() error {
|
||||
unlockFile(f.fd)
|
||||
return f.fd.Close()
|
||||
}
|
||||
|
||||
// Read 从文件中读取指定长度的内容
|
||||
// * size 长度
|
||||
// Read return 读取的内容,字符串格式
|
||||
func (f *File) Read(size int) (string, error) {
|
||||
str, err := f.ReadBytes(size)
|
||||
return string(str), err
|
||||
}
|
||||
|
||||
// ReadBytes 从二进制文件中读取指定长度的内容
|
||||
// ReadBytes return 读取的内容,二进制格式
|
||||
func (f *File) ReadBytes(size int) ([]byte, error) {
|
||||
buf := make([]byte, size)
|
||||
n, err := f.fd.Read(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf[0:n], nil
|
||||
}
|
||||
|
||||
// ReadAll 从文件中读取全部内容
|
||||
// ReadAll return 读取的内容,字符串格式
|
||||
func (f *File) ReadAll() (string, error) {
|
||||
str, err := f.ReadAllBytes()
|
||||
return string(str), err
|
||||
}
|
||||
|
||||
// ReadAllBytes 从二进制文件中读取全部内容
|
||||
// ReadAllBytes return 读取的内容,二进制格式
|
||||
func (f *File) ReadAllBytes() ([]byte, error) {
|
||||
var maxLen int
|
||||
if fi, _ := os.Stat(f.name); fi != nil {
|
||||
maxLen = int(fi.Size())
|
||||
} else {
|
||||
maxLen = 1024000
|
||||
}
|
||||
return f.ReadBytes(maxLen)
|
||||
}
|
||||
|
||||
// ReadLines 逐行文件中读取全部内容
|
||||
// ReadLines return 读取的内容,字符串数组格式
|
||||
func (f *File) ReadLines() ([]string, error) {
|
||||
outs := make([]string, 0)
|
||||
inputReader := bufio.NewReader(f.fd)
|
||||
for {
|
||||
line, err := inputReader.ReadString('\n')
|
||||
line = strings.TrimRight(line, "\r\n")
|
||||
outs = append(outs, line)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
return outs, nil
|
||||
}
|
||||
|
||||
// Write 写入字符串
|
||||
// Write return 写入的长度
|
||||
func (f *File) Write(content string) (int, error) {
|
||||
return f.WriteBytes([]byte(content))
|
||||
}
|
||||
|
||||
// WriteBytes 写入二进制
|
||||
// WriteBytes return 写入的长度
|
||||
func (f *File) WriteBytes(content []byte) (int, error) {
|
||||
return f.fd.Write(content)
|
||||
}
|
||||
|
||||
// SeekStart 将文件指针移动到开头
|
||||
func (f *File) SeekStart() error {
|
||||
_, err := f.fd.Seek(0, 0)
|
||||
return err
|
||||
}
|
||||
|
||||
// SeekEnd 将文件指针移动到末尾
|
||||
func (f *File) SeekEnd() error {
|
||||
_, err := f.fd.Seek(0, 2)
|
||||
return err
|
||||
}
|
||||
|
||||
// Seek 将文件指针移动到指定位置(从文件开头计算)
|
||||
func (f *File) Seek(offset int64) error {
|
||||
_, err := f.fd.Seek(offset, 0)
|
||||
return err
|
||||
}
|
||||
|
||||
func getNotAllowMessage(filename string) string {
|
||||
fileConfigLock.RLock()
|
||||
defer fileConfigLock.RUnlock()
|
||||
return notAllowMessage + ": " + filename
|
||||
}
|
||||
|
||||
func getAllowPaths() []string {
|
||||
fileConfigLock.RLock()
|
||||
defer fileConfigLock.RUnlock()
|
||||
allowPaths := make([]string, len(_allowPaths))
|
||||
for i, v := range _allowPaths {
|
||||
allowPaths[i] = v
|
||||
}
|
||||
return allowPaths
|
||||
}
|
||||
|
||||
func getAllowExtensions() []string {
|
||||
fileConfigLock.RLock()
|
||||
defer fileConfigLock.RUnlock()
|
||||
allowExtensions := make([]string, len(_allowExtensions))
|
||||
for i, v := range _allowPaths {
|
||||
allowExtensions[i] = v
|
||||
}
|
||||
return allowExtensions
|
||||
}
|
||||
|
||||
func checkDirAllow(filename string) bool {
|
||||
allowPaths := getAllowPaths()
|
||||
if len(allowPaths) > 0 {
|
||||
ok := false
|
||||
for _, allowPath := range allowPaths {
|
||||
if strings.HasPrefix(filename, allowPath) {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func checkFileAllow(filename string) bool {
|
||||
if !checkDirAllow(filename) {
|
||||
return false
|
||||
}
|
||||
|
||||
allowExtensions := getAllowExtensions()
|
||||
if len(allowExtensions) > 0 {
|
||||
ok := false
|
||||
for _, allowExtension := range allowExtensions {
|
||||
if strings.HasSuffix(filename, allowExtension) {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
module github.com/api-go/plugins
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/ZZMarquis/gm v1.3.2
|
||||
github.com/api-go/plugin v1.0.4
|
||||
github.com/emmansun/gmsm v0.21.1
|
||||
github.com/gorilla/websocket v1.5.1
|
||||
github.com/obscuren/ecies v0.0.0-20150213224233-7c0f4a9b18d9
|
||||
github.com/ssgo/db v0.6.11
|
||||
github.com/ssgo/discover v0.6.11
|
||||
github.com/ssgo/httpclient v0.6.11
|
||||
github.com/ssgo/log v0.6.11
|
||||
github.com/ssgo/redis v0.6.11
|
||||
github.com/ssgo/u v0.6.11
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/go-sql-driver/mysql v1.5.0 // indirect
|
||||
github.com/gomodule/redigo v1.8.8 // indirect
|
||||
github.com/mitchellh/mapstructure v1.4.1 // indirect
|
||||
github.com/ssgo/config v0.6.11 // indirect
|
||||
github.com/ssgo/standard v0.6.11 // indirect
|
||||
github.com/stretchr/testify v1.8.1 // indirect
|
||||
golang.org/x/crypto v0.14.0 // indirect
|
||||
golang.org/x/net v0.17.0 // indirect
|
||||
golang.org/x/sys v0.13.0 // indirect
|
||||
golang.org/x/text v0.13.0 // indirect
|
||||
)
|
|
@ -0,0 +1,367 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"github.com/api-go/plugin"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/ssgo/httpclient"
|
||||
"github.com/ssgo/log"
|
||||
"github.com/ssgo/u"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
pool *httpclient.ClientPool
|
||||
baseURL string
|
||||
globalHeaders map[string]string
|
||||
}
|
||||
|
||||
var defaultClient = Client{
|
||||
pool: httpclient.GetClient(time.Duration(0) * time.Second),
|
||||
globalHeaders: map[string]string{},
|
||||
}
|
||||
|
||||
func init() {
|
||||
defaultClient.pool.EnableRedirect()
|
||||
plugin.Register(plugin.Plugin{
|
||||
Id: "http",
|
||||
Name: "HTTP客户端",
|
||||
Objects: map[string]interface{}{
|
||||
"new": NewHTTP,
|
||||
"newH2C": NewH2CHTTP,
|
||||
"newWithoutRedirect": NewHTTPWithoutRedirect,
|
||||
"newH2CWithoutRedirect": NewH2CHTTPWithoutRedirect,
|
||||
"setBaseURL": defaultClient.SetBaseURL,
|
||||
"SetGlobalHeaders": defaultClient.SetGlobalHeaders,
|
||||
"get": defaultClient.Get,
|
||||
"post": defaultClient.Post,
|
||||
"put": defaultClient.Put,
|
||||
"delete": defaultClient.Delete,
|
||||
"head": defaultClient.Head,
|
||||
"do": defaultClient.Do,
|
||||
"manualDo": defaultClient.ManualDo,
|
||||
"open": defaultClient.Open,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// SetBaseURL 设置一个URL前缀,后续请求中可以只提供path部分
|
||||
// SetBaseURL url 以http://或https://开头的URL地址
|
||||
func (c *Client) SetBaseURL(url string) {
|
||||
c.baseURL = url
|
||||
}
|
||||
|
||||
// SetGlobalHeaders 设置固定的HTTP头部信息,在每个请求中都加入这些HTTP头
|
||||
// SetGlobalHeaders headers 传入一个Key-Value对象的HTTP头信息
|
||||
func (c *Client) SetGlobalHeaders(headers map[string]string) {
|
||||
c.globalHeaders = headers
|
||||
}
|
||||
|
||||
// Get 发送GET请求
|
||||
// * url 以http://或https://开头的URL地址,如果设置了baseURL可以只提供path部分
|
||||
// * headers 传入一个Key-Value对象的HTTP头信息,如果不指定头信息这个参数可以省略不传
|
||||
// * return 返回结果对象,如果返回值是JSON格式,将自动转化为对象否则将字符串放在.result中,如发生错误将抛出异常,返回的对象中还包括:headers、statusCode、statusMessage
|
||||
func (c *Client) Get(logger *log.Logger, url string, headers *map[string]string) (map[string]interface{}, error) {
|
||||
return makeResult(logger, c.pool.Get(c.makeURL(url), c.makeHeaderArray(headers)...))
|
||||
}
|
||||
|
||||
// Post 发送POST请求
|
||||
// * body 可以传入任意类型,如果不是字符串或二进制数组时会自动添加application/json头,数据将以json格式发送
|
||||
func (c *Client) Post(logger *log.Logger, url string, body interface{}, headers *map[string]string) (map[string]interface{}, error) {
|
||||
return makeResult(logger, c.pool.Post(c.makeURL(url), body, c.makeHeaderArray(headers)...))
|
||||
}
|
||||
|
||||
// Put 发送PUT请求
|
||||
func (c *Client) Put(logger *log.Logger, url string, body interface{}, headers *map[string]string) (map[string]interface{}, error) {
|
||||
return makeResult(logger, c.pool.Put(c.makeURL(url), body, c.makeHeaderArray(headers)...))
|
||||
}
|
||||
|
||||
// Delete 发送DELETE请求
|
||||
func (c *Client) Delete(logger *log.Logger, url string, body interface{}, headers *map[string]string) (map[string]interface{}, error) {
|
||||
return makeResult(logger, c.pool.Delete(c.makeURL(url), body, c.makeHeaderArray(headers)...))
|
||||
}
|
||||
|
||||
// Head 发送HEAD请求
|
||||
func (c *Client) Head(logger *log.Logger, url string, headers *map[string]string) (map[string]interface{}, error) {
|
||||
return makeResult(logger, c.pool.Head(c.makeURL(url), c.makeHeaderArray(headers)...))
|
||||
}
|
||||
|
||||
// Do 发送请求
|
||||
// * method 请求方法,GET、POST等
|
||||
func (c *Client) Do(logger *log.Logger, method string, url string, body interface{}, headers *map[string]string) (map[string]interface{}, error) {
|
||||
return makeResult(logger, c.pool.Do(method, c.makeURL(url), body, c.makeHeaderArray(headers)...))
|
||||
}
|
||||
|
||||
// ManualDo 手动处理请求,需要自行从返回结果中读取数据,可实现SSE客户端
|
||||
// ManualDo return 应答的对象(需手动读取数据并关闭请求)
|
||||
func (c *Client) ManualDo(logger *log.Logger, method string, url string, body interface{}, headers *map[string]string) (*Reader, error) {
|
||||
result := c.pool.ManualDo(method, url, body, c.makeHeaderArray(headers)...)
|
||||
err, outHeaders, statusCode, _ := _makeResult(logger, result)
|
||||
return &Reader{
|
||||
Error: err,
|
||||
Headers: outHeaders,
|
||||
StatusCode: statusCode,
|
||||
response: result.Response,
|
||||
logger: logger,
|
||||
}, result.Error
|
||||
}
|
||||
|
||||
// Open 打开一个Websocket连接
|
||||
// Open return Websocket对象(使用完毕请关闭连接)
|
||||
func (c *Client) Open(logger *log.Logger, url string, headers *map[string]string) (*WS, error) {
|
||||
reqHeader := http.Header{}
|
||||
if headers != nil {
|
||||
for k, v := range *headers {
|
||||
reqHeader.Set(k, v)
|
||||
}
|
||||
}
|
||||
if conn, _, err := websocket.DefaultDialer.Dial(url, reqHeader); err == nil {
|
||||
logger.Error(err.Error())
|
||||
return &WS{conn: conn, logger: logger}, err
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func makeResult(logger *log.Logger, result *httpclient.Result) (map[string]interface{}, error) {
|
||||
err, headers, statusCode, output := _makeResult(logger, result)
|
||||
if v, ok := output.(map[string]interface{}); ok {
|
||||
if err != "" {
|
||||
v["error"] = err
|
||||
}
|
||||
v["headers"] = headers
|
||||
v["statusCode"] = statusCode
|
||||
return v, result.Error
|
||||
} else {
|
||||
return map[string]interface{}{
|
||||
"error": err,
|
||||
"headers": headers,
|
||||
"statusCode": statusCode,
|
||||
"result": output,
|
||||
}, result.Error
|
||||
}
|
||||
}
|
||||
|
||||
func _makeResult(logger *log.Logger, result *httpclient.Result) (err string, headers map[string]string, statusCode int, output interface{}) {
|
||||
if result.Error != nil {
|
||||
err = result.Error.Error()
|
||||
logger.Error(result.Error.Error())
|
||||
}
|
||||
|
||||
if result.Response != nil {
|
||||
headers = map[string]string{}
|
||||
for k, v := range result.Response.Header {
|
||||
if len(v) == 1 {
|
||||
headers[k] = v[0]
|
||||
} else {
|
||||
headers[k] = strings.Join(v, " ")
|
||||
}
|
||||
}
|
||||
statusCode = result.Response.StatusCode
|
||||
|
||||
if strings.Contains(result.Response.Header.Get("Content-Type"), "application/json") {
|
||||
output = map[string]interface{}{}
|
||||
u.UnJson(result.String(), &output)
|
||||
} else {
|
||||
output = result.String()
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//func makeResult(logger *log.Logger, result *httpclient.Result) (map[string]interface{}, error) {
|
||||
// r := map[string]interface{}{}
|
||||
// if result.Error != nil {
|
||||
// logger.Error(result.Error.Error())
|
||||
// return nil, result.Error
|
||||
// }
|
||||
//
|
||||
// if result.Response != nil {
|
||||
// headers := map[string]string{}
|
||||
// for k, v := range result.Response.Header {
|
||||
// if len(v) == 1 {
|
||||
// headers[k] = v[0]
|
||||
// } else {
|
||||
// headers[k] = strings.Join(v, " ")
|
||||
// }
|
||||
// }
|
||||
// r["headers"] = headers
|
||||
// r["statusCode"] = result.Response.StatusCode
|
||||
// r["statusMessage"] = result.Response.Status
|
||||
//
|
||||
// if strings.Contains(result.Response.Header.Get("Content-Type"), "application/json") {
|
||||
// u.UnJson(result.String(), &r)
|
||||
// } else {
|
||||
// r["result"] = result.String()
|
||||
// }
|
||||
// }
|
||||
// return r, nil
|
||||
//}
|
||||
|
||||
func (c *Client) makeURL(url string) string {
|
||||
if !strings.Contains(url, "://") && c.baseURL != "" {
|
||||
if strings.HasSuffix(c.baseURL, "/") && strings.HasPrefix(url, "/") {
|
||||
return c.baseURL + url[1:]
|
||||
} else if !strings.HasSuffix(c.baseURL, "/") && !strings.HasPrefix(url, "/") {
|
||||
return c.baseURL + "/" + url
|
||||
}
|
||||
return c.baseURL + url
|
||||
}
|
||||
return url
|
||||
}
|
||||
|
||||
func (c *Client) makeHeaderArray(in *map[string]string) []string {
|
||||
out := make([]string, 0)
|
||||
if c.globalHeaders != nil {
|
||||
for k, v := range c.globalHeaders {
|
||||
out = append(out, k, v)
|
||||
}
|
||||
}
|
||||
if in != nil {
|
||||
for k, v := range *in {
|
||||
out = append(out, k, v)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// NewHTTP 创建新的HTTP客户端
|
||||
// * timeout 请求的超时时间,单位(毫秒)
|
||||
// NewHTTP return HTTP客户端对象
|
||||
func NewHTTP(timeout int) *Client {
|
||||
pool := httpclient.GetClient(time.Duration(timeout) * time.Second)
|
||||
pool.EnableRedirect()
|
||||
return &Client{
|
||||
pool: pool,
|
||||
globalHeaders: map[string]string{},
|
||||
}
|
||||
}
|
||||
|
||||
// NewHTTPWithoutRedirect 创建新的HTTP客户端(不自动跟踪301和302跳转)
|
||||
// * timeout 请求的超时时间,单位(毫秒)
|
||||
// NewHTTPWithoutRedirect return HTTP客户端对象
|
||||
func NewHTTPWithoutRedirect(timeout int) *Client {
|
||||
pool := httpclient.GetClient(time.Duration(timeout) * time.Second)
|
||||
return &Client{
|
||||
pool: pool,
|
||||
globalHeaders: map[string]string{},
|
||||
}
|
||||
}
|
||||
|
||||
// NewH2CHTTP 创建新的H2C客户端
|
||||
// NewH2CHTTP return H2C客户端对象
|
||||
func NewH2CHTTP(timeout int) *Client {
|
||||
pool := httpclient.GetClientH2C(time.Duration(timeout) * time.Second)
|
||||
pool.EnableRedirect()
|
||||
return &Client{
|
||||
pool: pool,
|
||||
globalHeaders: map[string]string{},
|
||||
}
|
||||
}
|
||||
|
||||
// NewH2CHTTPWithoutRedirect 创建新的H2C客户端(不自动跟踪301和302跳转)
|
||||
// NewH2CHTTPWithoutRedirect return H2C客户端对象
|
||||
func NewH2CHTTPWithoutRedirect(timeout int) *Client {
|
||||
pool := httpclient.GetClientH2C(time.Duration(timeout) * time.Second)
|
||||
return &Client{
|
||||
pool: pool,
|
||||
globalHeaders: map[string]string{},
|
||||
}
|
||||
}
|
||||
|
||||
type WS struct {
|
||||
conn *websocket.Conn
|
||||
closed bool
|
||||
logger *log.Logger
|
||||
}
|
||||
|
||||
// Read 读取文本数据
|
||||
// Read return 读取到的字符串
|
||||
func (ws *WS) Read() (string, error) {
|
||||
_, buf, err := ws.conn.ReadMessage()
|
||||
return string(buf), err
|
||||
}
|
||||
|
||||
// ReadBytes 读取二进制数据
|
||||
// ReadBytes return 读取到的二进制数据
|
||||
func (ws *WS) ReadBytes() ([]byte, error) {
|
||||
_, buf, err := ws.conn.ReadMessage()
|
||||
return buf, err
|
||||
}
|
||||
|
||||
// ReadJSON 读取JSON对象
|
||||
// ReadJSON return 读取到的对象
|
||||
func (ws *WS) ReadJSON() (interface{}, error) {
|
||||
var obj interface{}
|
||||
err := ws.conn.ReadJSON(&obj)
|
||||
return obj, err
|
||||
}
|
||||
|
||||
// Write 写入文本数据
|
||||
// Write content 文本数据
|
||||
func (ws *WS) Write(content string) error {
|
||||
return ws.conn.WriteMessage(websocket.TextMessage, []byte(content))
|
||||
}
|
||||
|
||||
// WriteBytes 写入二进制数据
|
||||
// WriteBytes content 二进制数据
|
||||
func (ws *WS) WriteBytes(content []byte) error {
|
||||
return ws.conn.WriteMessage(websocket.BinaryMessage, content)
|
||||
}
|
||||
|
||||
// WriteJSON 写入对象
|
||||
// WriteJSON content 对象
|
||||
func (ws *WS) WriteJSON(content interface{}) error {
|
||||
return ws.conn.WriteJSON(content)
|
||||
}
|
||||
|
||||
//// OnClose 关闭事件
|
||||
//// OnClose callback 对方关闭时调用
|
||||
//func (ws *WS) OnClose(callback func()) {
|
||||
// ws.conn.SetCloseHandler(func(code int, text string) error {
|
||||
// callback()
|
||||
// return nil
|
||||
// })
|
||||
//}
|
||||
|
||||
// Close 关闭连接
|
||||
func (ws *WS) Close() error {
|
||||
if ws.closed {
|
||||
return nil
|
||||
}
|
||||
ws.closed = true
|
||||
return ws.conn.Close()
|
||||
}
|
||||
|
||||
// EnableCompression 启用压缩
|
||||
func (ws *WS) EnableCompression() {
|
||||
ws.conn.EnableWriteCompression(true)
|
||||
}
|
||||
|
||||
type Reader struct {
|
||||
Error string
|
||||
Headers map[string]string
|
||||
StatusCode int
|
||||
response *http.Response
|
||||
closed bool
|
||||
logger *log.Logger
|
||||
}
|
||||
|
||||
func (hr *Reader) Read(n int) (string, error) {
|
||||
buf := make([]byte, n)
|
||||
n1, err := hr.response.Body.Read(buf)
|
||||
return string(buf[0:n1]), err
|
||||
}
|
||||
func (hr *Reader) ReadBytes(n int) ([]byte, error) {
|
||||
buf := make([]byte, n)
|
||||
n1, err := hr.response.Body.Read(buf)
|
||||
return buf[0:n1], err
|
||||
}
|
||||
func (hr *Reader) Close() error {
|
||||
if hr.closed {
|
||||
return nil
|
||||
}
|
||||
hr.closed = true
|
||||
return hr.response.Body.Close()
|
||||
}
|
|
@ -0,0 +1,309 @@
|
|||
package redis
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/api-go/plugin"
|
||||
"github.com/ssgo/log"
|
||||
"github.com/ssgo/redis"
|
||||
"github.com/ssgo/u"
|
||||
)
|
||||
|
||||
type Redis struct {
|
||||
pool *redis.Redis
|
||||
}
|
||||
|
||||
var redisPool = map[string]*redis.Redis{}
|
||||
var defaultRedis *redis.Redis
|
||||
|
||||
func init() {
|
||||
plugin.Register(plugin.Plugin{
|
||||
Id: "redis",
|
||||
Name: "Redis客户端",
|
||||
ConfigSample: `default: redis://:<**encrypted_password**>@127.0.0.1:6379/1?timeout=10s&logSlow=100ms # set default redis connection pool, used by redis.xxx
|
||||
configs:
|
||||
conn1: redis://127.0.0.1:6379/12 # set a named connection pool, used by redis.get('conn1').xxx
|
||||
`,
|
||||
Init: func(conf map[string]interface{}) {
|
||||
if conf["default"] != nil {
|
||||
defaultRedis = redis.GetRedis(u.String(conf["default"]), nil)
|
||||
}
|
||||
if conf["configs"] != nil {
|
||||
confs := map[string]string{}
|
||||
u.Convert(conf["configs"], &confs)
|
||||
for name, url := range confs {
|
||||
redisPool[name] = redis.GetRedis(url, nil)
|
||||
}
|
||||
}
|
||||
},
|
||||
Objects: map[string]interface{}{
|
||||
"fetch": GetRedis,
|
||||
},
|
||||
// 实现直接使用redis.xxx操作默认的Redis
|
||||
JsCode: `let _redis = redis
|
||||
redis = _redis.fetch()
|
||||
redis.fetch = _redis.fetch
|
||||
`,
|
||||
})
|
||||
}
|
||||
|
||||
// GetRedis 获得Redis连接
|
||||
// GetRedis name 连接配置名称,如果不提供名称则使用默认连接
|
||||
// GetRedis return Redis连接,对象内置连接池操作,完成后无需手动关闭连接
|
||||
func GetRedis(name *string, logger *log.Logger) *Redis {
|
||||
if name == nil || *name == "" {
|
||||
if defaultRedis != nil {
|
||||
return &Redis{pool: defaultRedis.CopyByLogger(logger)}
|
||||
}
|
||||
} else {
|
||||
if redisPool[*name] != nil {
|
||||
return &Redis{pool: redisPool[*name].CopyByLogger(logger)}
|
||||
} else if defaultRedis != nil {
|
||||
return &Redis{pool: defaultRedis.CopyByLogger(logger)}
|
||||
}
|
||||
}
|
||||
return &Redis{
|
||||
pool: redis.GetRedis("", logger),
|
||||
}
|
||||
}
|
||||
|
||||
func makeRedisResult(r *redis.Result) interface{} {
|
||||
var v interface{}
|
||||
buf := r.Bytes()
|
||||
if json.Unmarshal(buf, &v) == nil {
|
||||
return v
|
||||
} else {
|
||||
return string(buf)
|
||||
}
|
||||
}
|
||||
|
||||
func makeRedisResults(rr *redis.Result) []interface{} {
|
||||
out := make([]interface{}, 0)
|
||||
for _, r := range rr.Results() {
|
||||
out = append(out, makeRedisResult(&r))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func makeRedisResultMap(rr *redis.Result) map[string]interface{} {
|
||||
out := map[string]interface{}{}
|
||||
for k, r := range rr.ResultMap() {
|
||||
out[k] = makeRedisResult(r)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Do 执行Redis操作,这是底层接口
|
||||
// Do cmd 命令
|
||||
// Do values 参数,根据命令传入不同参数
|
||||
// Do return 返回字符串数据,需要根据数据内容自行解析处理
|
||||
func (rd *Redis) Do(cmd string, values ...interface{}) string {
|
||||
return rd.pool.Do(cmd, values...).String()
|
||||
}
|
||||
|
||||
// Del 删除
|
||||
// Del keys 传入一个或多个Key
|
||||
// Del return 成功删除的个数
|
||||
func (rd *Redis) Del(keys ...string) int {
|
||||
return rd.pool.Do("DEL", u.ToInterfaceArray(keys)...).Int()
|
||||
}
|
||||
|
||||
// Exists 判断是否Key存在
|
||||
// * key 指定一个Key
|
||||
// Exists return 是否存在
|
||||
func (rd *Redis) Exists(key string) bool {
|
||||
return rd.pool.Do("EXISTS " + key).Bool()
|
||||
}
|
||||
|
||||
// Expire 设置Key的过期时间
|
||||
// * seconds 过期时间的秒数
|
||||
// Expire return 是否成功
|
||||
func (rd *Redis) Expire(key string, seconds int) bool {
|
||||
return rd.pool.Do("EXPIRE "+key, seconds).Bool()
|
||||
}
|
||||
|
||||
// ExpireAt 设置Key的过期时间(指定具体时间)
|
||||
// ExpireAt time 过期时间的时间戳,单位秒
|
||||
// ExpireAt return 是否成功
|
||||
func (rd *Redis) ExpireAt(key string, time int) bool {
|
||||
return rd.pool.Do("EXPIREAT "+key, time).Bool()
|
||||
}
|
||||
|
||||
// Keys 查询Key
|
||||
// * patten 查询条件,例如:"SESS_*"
|
||||
// * return []string 查询到的Key列表
|
||||
func (rd *Redis) Keys(patten string) []string {
|
||||
return rd.pool.Do("KEYS " + patten).Strings()
|
||||
}
|
||||
|
||||
// Get 读取Key的内容
|
||||
// * return any 如果是一个对象则返回反序列化后的对象,否则返回字符串
|
||||
func (rd *Redis) Get(key string) interface{} {
|
||||
return makeRedisResult(rd.pool.Do("GET " + key))
|
||||
}
|
||||
|
||||
// GetEX 读取Key的内容并更新过期时间
|
||||
func (rd *Redis) GetEX(key string, seconds int) interface{} {
|
||||
r := rd.pool.Do("GET " + key)
|
||||
if r.String() != "" {
|
||||
rd.pool.EXPIRE(key, seconds)
|
||||
}
|
||||
return makeRedisResult(r)
|
||||
}
|
||||
|
||||
// Set 存储内容到Key
|
||||
// * value 对象或字符串
|
||||
// * return bool 是否成功
|
||||
func (rd *Redis) Set(key string, value interface{}) bool {
|
||||
return rd.pool.Do("SET "+key, value).Bool()
|
||||
}
|
||||
// SetEX 存储内容到Key并设置过期时间
|
||||
func (rd *Redis) SetEX(key string, seconds int, value interface{}) bool {
|
||||
return rd.pool.Do("SETEX "+key, seconds, value).Bool()
|
||||
}
|
||||
// SetNX 存储内容到一个不存在的Key,如果Key已经存在则设置失败
|
||||
func (rd *Redis) SetNX(key string, value interface{}) bool {
|
||||
return rd.pool.Do("SETNX "+key, value).Bool()
|
||||
}
|
||||
// GetSet 将给定 key 的值设为 value ,并返回 key 的旧值(old value)
|
||||
func (rd *Redis) GetSet(key string, value interface{}) interface{} {
|
||||
return makeRedisResult(rd.pool.Do("GETSET "+key, value))
|
||||
}
|
||||
|
||||
// Incr 将 key 中储存的数值增一
|
||||
// Incr * int64 最新的计数
|
||||
func (rd *Redis) Incr(key string) int64 {
|
||||
return rd.pool.Do("INCR " + key).Int64()
|
||||
}
|
||||
// Decr 将 key 中储存的数值减一
|
||||
func (rd *Redis) Decr(key string) int64 {
|
||||
return rd.pool.Do("DECR " + key).Int64()
|
||||
}
|
||||
// IncrBy 将 key 中储存的数值加上增量 increment
|
||||
func (rd *Redis) IncrBy(key string, increment int64) int64 {
|
||||
return rd.pool.Do("INCRBY " + key, increment).Int64()
|
||||
}
|
||||
// DecrBy 将 key 中储存的数值减去加上增量 increment
|
||||
func (rd *Redis) DecrBy(key string, increment int64) int64 {
|
||||
return rd.pool.Do("DECRBY " + key, increment).Int64()
|
||||
}
|
||||
|
||||
// MGet 获取所有(一个或多个)给定 key 的值
|
||||
// MGet return []any 按照查询key的顺序返回结果,如果结果是一个对象则返回反序列化后的对象,否则返回字符串
|
||||
func (rd *Redis) MGet(keys ...string) []interface{} {
|
||||
return makeRedisResults(rd.pool.Do("MGET", u.ToInterfaceArray(keys)...))
|
||||
}
|
||||
// MSet 同时设置一个或多个 key-value 对
|
||||
// MSet keyAndValues 按照key-value的顺序依次传入一个或多个数据
|
||||
func (rd *Redis) MSet(keyAndValues ...interface{}) bool {
|
||||
return rd.pool.Do("MSET", keyAndValues...).Bool()
|
||||
}
|
||||
|
||||
// HGet 获取存储在哈希表中指定字段的值
|
||||
// * field 字段
|
||||
func (rd *Redis) HGet(key, field string) interface{} {
|
||||
return makeRedisResult(rd.pool.Do("HGET "+key, field))
|
||||
}
|
||||
// HSet 将哈希表 key 中的字段 field 的值设为 value
|
||||
func (rd *Redis) HSet(key, field string, value interface{}) bool {
|
||||
return rd.pool.Do("HSET "+key, field, value).Bool()
|
||||
}
|
||||
|
||||
// HSetNX 只有在字段 field 不存在时,设置哈希表字段的值
|
||||
func (rd *Redis) HSetNX(key, field string, value interface{}) bool {
|
||||
return rd.pool.Do("HSETNX "+key, field, value).Bool()
|
||||
}
|
||||
// HMGet 获取所有给定字段的值
|
||||
// * fields 字段列表
|
||||
func (rd *Redis) HMGet(key string, fields ...string) []interface{} {
|
||||
return makeRedisResults(rd.pool.Do("HMGET", append(append([]interface{}{}, key), u.ToInterfaceArray(fields)...)...))
|
||||
}
|
||||
// HGetAll 获取在哈希表中指定 key 的所有字段和值
|
||||
// HGetAll return 返回所有字段的值,如果值是一个对象则返回反序列化后的对象,否则返回字符串
|
||||
func (rd *Redis) HGetAll(key string) map[string]interface{} {
|
||||
return makeRedisResultMap(rd.pool.Do("HGETALL " + key))
|
||||
}
|
||||
// HMSet 将哈希表 key 中的字段 field 的值设为 value
|
||||
func (rd *Redis) HMSet(key string, fieldAndValues ...interface{}) bool {
|
||||
return rd.pool.Do("HMSET", append(append([]interface{}{}, key), fieldAndValues...)...).Bool()
|
||||
}
|
||||
// HKeys 获取所有哈希表中的字段
|
||||
func (rd *Redis) HKeys(patten string) []string {
|
||||
return rd.pool.Do("HKEYS " + patten).Strings()
|
||||
}
|
||||
// HLen 获取哈希表中字段的数量
|
||||
// HLen return 字段数量
|
||||
func (rd *Redis) HLen(key string) int {
|
||||
return rd.pool.Do("HLEN " + key).Int()
|
||||
}
|
||||
// HDel 删除一个或多个哈希表字段
|
||||
// HDel return 成功删除的个数
|
||||
func (rd *Redis) HDel(key string, fields ...string) int {
|
||||
return rd.pool.Do("HDEL", append(append([]interface{}{}, key), u.ToInterfaceArray(fields)...)...).Int()
|
||||
}
|
||||
// HExists 查看哈希表 key 中,指定的字段是否存在
|
||||
func (rd *Redis) HExists(key, field string) bool {
|
||||
return rd.pool.Do("HEXISTS "+key, field).Bool()
|
||||
}
|
||||
// HIncr 为哈希表 key 中的指定字段的整数值加上增量1
|
||||
func (rd *Redis) HIncr(key, field string) int64 {
|
||||
return rd.pool.Do("HINCRBY "+key, field, 1).Int64()
|
||||
}
|
||||
// HDecr 为哈希表 key 中的指定字段的整数值减去增量1
|
||||
func (rd *Redis) HDecr(key, field string) int64 {
|
||||
return rd.pool.Do("HDECRBY "+key, field, 1).Int64()
|
||||
}
|
||||
// HIncrBy 为哈希表 key 中的指定字段的整数值加上增量 increment
|
||||
func (rd *Redis) HIncrBy(key, field string, increment int64) int64 {
|
||||
return rd.pool.Do("HINCRBY "+key, field, increment).Int64()
|
||||
}
|
||||
// HDecrBy 为哈希表 key 中的指定字段的整数值减去增量 increment
|
||||
func (rd *Redis) HDecrBy(key, field string, increment int64) int64 {
|
||||
return rd.pool.Do("HDECRBY "+key, field, increment).Int64()
|
||||
}
|
||||
|
||||
// LPush 将一个或多个值插入到列表头部
|
||||
// LPush return 成功添加的个数
|
||||
func (rd *Redis) LPush(key string, values ...string) int {
|
||||
return rd.pool.Do("LPUSH", append(append([]interface{}{}, key), u.ToInterfaceArray(values)...)...).Int()
|
||||
}
|
||||
// RPush 在列表中添加一个或多个值
|
||||
// RPush return 成功添加的个数
|
||||
func (rd *Redis) RPush(key string, values ...string) int {
|
||||
return rd.pool.Do("RPUSH", append(append([]interface{}{}, key), u.ToInterfaceArray(values)...)...).Int()
|
||||
}
|
||||
// LPop 移出并获取列表的第一个元素
|
||||
func (rd *Redis) LPop(key string) interface{} {
|
||||
return makeRedisResult(rd.pool.Do("LPOP " + key))
|
||||
}
|
||||
// RPop 移除并获取列表最后一个元素
|
||||
func (rd *Redis) RPop(key string) interface{} {
|
||||
return makeRedisResult(rd.pool.Do("RPOP " + key))
|
||||
}
|
||||
// LLen 获取列表长度
|
||||
// LLen 列表的长度
|
||||
func (rd *Redis) LLen(key string) int {
|
||||
return rd.pool.Do("LLEN " + key).Int()
|
||||
}
|
||||
// LRange 获取列表指定范围内的元素
|
||||
// LRange return []any 列表数据,如果值是一个对象则返回反序列化后的对象,否则返回字符串
|
||||
func (rd *Redis) LRange(key string, start, stop int) []interface{} {
|
||||
return makeRedisResults(rd.pool.Do("LRANGE "+key, start, stop))
|
||||
}
|
||||
|
||||
// TODO 支持订阅,还需要支持 Start、Stop、向Context注册析构函数确保Stop被执行,需要支持传入Function转化为func
|
||||
// Subscribe
|
||||
//func (rd *Redis) Subscribe(channel string, onReceived func([]byte)) bool {
|
||||
// return rd.pool.Subscribe(channel, nil, onReceived)
|
||||
//}
|
||||
//
|
||||
// Unsubscribe
|
||||
//func (rd *Redis) Unsubscribe(channel string) bool {
|
||||
// return rd.pool.Unsubscribe(channel)
|
||||
//}
|
||||
|
||||
// Publish 将信息发送到指定的频道
|
||||
// Publish channel 渠道名称
|
||||
// Publish data 数据,字符串格式
|
||||
func (rd *Redis) Publish(channel, data string) bool {
|
||||
return rd.pool.Do("PUBLISH "+channel, data).Bool()
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package runtime
|
||||
|
||||
import (
|
||||
"github.com/api-go/plugin"
|
||||
"github.com/ssgo/u"
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
plugin.Register(plugin.Plugin{
|
||||
Id: "runtime",
|
||||
Name: "运行时支持",
|
||||
Objects: map[string]interface{}{
|
||||
// sleep 程序等待指定时间
|
||||
// sleep ms 休眠时长,单位ms
|
||||
"sleep": func(ms int) {
|
||||
time.Sleep(time.Duration(ms) * time.Millisecond)
|
||||
},
|
||||
// os 获取操作系统名称
|
||||
// os return 操作系统名称
|
||||
"os": func() string {
|
||||
return runtime.GOOS
|
||||
},
|
||||
// arch 获取操作系统构建版本
|
||||
// arch return 操作系统构建版本
|
||||
"arch": func() string {
|
||||
return runtime.GOARCH
|
||||
},
|
||||
// shell 运行外部命令
|
||||
// shell command 命令
|
||||
// shell args 参数
|
||||
// shell return 运行结果
|
||||
"shell": func(command string, args ...string) ([]string, error) {
|
||||
return u.RunCommand(command, args...)
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
|
@ -0,0 +1,356 @@
|
|||
package util
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"github.com/api-go/plugin"
|
||||
"github.com/ssgo/u"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func init() {
|
||||
plugin.Register(plugin.Plugin{
|
||||
Id: "util",
|
||||
Name: "基础工具",
|
||||
JsCode: `
|
||||
util.formatDate = function (date, fmt, timezone) {
|
||||
let ret
|
||||
if (!date) return ''
|
||||
if (!fmt) fmt = 'YYYY-mm-dd HH:MM:SS'
|
||||
const opt = {
|
||||
'YYYY': date.getFullYear().toString(),
|
||||
'YY': date.getFullYear().toString().substring(2),
|
||||
'm+': (date.getMonth() + 1).toString(),
|
||||
'd+': date.getDate().toString(),
|
||||
'H+': date.getHours().toString(),
|
||||
'h+': (date.getHours() % 12).toString(),
|
||||
'APM': (date.getHours() > 12 ? 'PM' : 'AM'),
|
||||
'M+': date.getMinutes().toString(),
|
||||
'S+': date.getSeconds().toString(),
|
||||
'YW': 'W' + Math.ceil(((date.getTime() - new Date(date.getFullYear(), 0, 0))) / (24 * 60 * 60 * 1000) / 7),
|
||||
}
|
||||
for (let k in opt) {
|
||||
ret = new RegExp('(' + k + ')').exec(fmt)
|
||||
if (ret) {
|
||||
fmt = fmt.replace(ret[1], (ret[1].length == 1) ? (opt[k]) : (opt[k].padStart(ret[1].length, '0')))
|
||||
}
|
||||
}
|
||||
return fmt
|
||||
}
|
||||
|
||||
util.parseDate = function (str) {
|
||||
if (typeof str === 'number') str = str + ''
|
||||
if (/^\d+[\\.]?\d*$/.test(str)) {
|
||||
if (str.length === 10) str += '000'
|
||||
return new Date(util.int(str))
|
||||
}
|
||||
if (str.length === 19) {
|
||||
return new Date(parseInt(str.substr(0, 4)), parseInt(str.substr(5, 2)) - 1, parseInt(str.substr(8, 2)), parseInt(str.substr(11, 2)), parseInt(str.substr(14, 2)), parseInt(str.substr(17, 2)))
|
||||
} else if (str.length === 10) {
|
||||
return new Date(parseInt(str.substr(0, 4)), parseInt(str.substr(5, 2)) - 1, parseInt(str.substr(8, 2)))
|
||||
} else if (str.length === 7) {
|
||||
return new Date(parseInt(str.substr(0, 4)), parseInt(str.substr(5, 2)) - 1, 1)
|
||||
} else if (str.length === 8) {
|
||||
let date = new Date()
|
||||
return new Date(date.getFullYear(), date.getMonth(), date.getDate(), parseInt(str.substr(0, 2)), parseInt(str.substr(3, 2)), parseInt(str.substr(6, 2)))
|
||||
} else if (str.length === 5) {
|
||||
let date = new Date()
|
||||
return new Date(date.getFullYear(), date.getMonth(), date.getDate(), parseInt(str.substr(0, 2)), parseInt(str.substr(3, 2)), 0)
|
||||
}
|
||||
return new Date(str)
|
||||
}
|
||||
|
||||
util.int = function (v) {
|
||||
if (!v) return 0
|
||||
if (typeof v === 'number' || v instanceof Number) return v
|
||||
if (typeof v === 'object') v = v.toString ? v.toString() : ''
|
||||
if (typeof v !== 'string') return 0
|
||||
try {
|
||||
return Math.round(parseFloat(v.replace(/,/g, '').trim())) || 0
|
||||
} catch (e) {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
util.float = function (v) {
|
||||
if (!v) return 0.0
|
||||
if (typeof v === 'number' || v instanceof Number) return v
|
||||
if (typeof v === 'object') v = v.toString ? v.toString() : ''
|
||||
if (typeof v !== 'string') return 0.0
|
||||
try {
|
||||
return parseFloat(v.replace(/,/g, '').trim()) || 0.0
|
||||
} catch (e) {
|
||||
return 0.0
|
||||
}
|
||||
}
|
||||
|
||||
util.str = function (v) {
|
||||
if (!v) return ''
|
||||
if (typeof v === 'string') return v
|
||||
if (v.toString) return v.toString()
|
||||
return ''
|
||||
}
|
||||
|
||||
util.keysBy = function (obj, ...fieldAndValues) {
|
||||
let keys = []
|
||||
for (let k in obj) {
|
||||
let match = true
|
||||
if (fieldAndValues.length === 1) {
|
||||
// 查找一位数组
|
||||
if (obj[k] != fieldAndValues[0]) {
|
||||
match = false
|
||||
}
|
||||
} else {
|
||||
// 查找二维数组
|
||||
for (let i = 0; i < fieldAndValues.length; i += 2) {
|
||||
if (obj[k][fieldAndValues[i]] != fieldAndValues[i + 1]) {
|
||||
match = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
keys.push(k)
|
||||
}
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
util.listBy = function (obj, ...fieldAndValues) {
|
||||
let list = obj instanceof Array || obj instanceof NodeList ? [] : {}
|
||||
let keys = util.keysBy(obj, ...fieldAndValues)
|
||||
for (let k of keys) {
|
||||
if (obj instanceof Array || obj instanceof NodeList) {
|
||||
list.push(obj[k])
|
||||
} else {
|
||||
list[k] = obj[k]
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
util.hasBy = function (obj, ...fieldAndValues) {
|
||||
let keys = util.keysBy(obj, ...fieldAndValues)
|
||||
return keys.length > 0
|
||||
}
|
||||
|
||||
util.getBy = function (obj, ...fieldAndValues) {
|
||||
let keys = util.keysBy(obj, ...fieldAndValues)
|
||||
if (keys.length > 0) return obj[keys[0]]
|
||||
return null
|
||||
}
|
||||
|
||||
util.setBy = function (obj, value, ...fieldAndValues) {
|
||||
let keys = util.keysBy(obj, ...fieldAndValues)
|
||||
if (keys.length > 0) obj[keys[0]] = value
|
||||
}
|
||||
|
||||
util.indexBy = function (obj, ...fieldAndValues) {
|
||||
let keys = util.keysBy(obj, ...fieldAndValues)
|
||||
if (keys.length > 0) {
|
||||
return obj instanceof Array || obj instanceof NodeList ? util.int(keys[0]) : keys[0]
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
util.removeBy = function (obj, ...fieldAndValues) {
|
||||
let keys = util.keysBy(obj, ...fieldAndValues)
|
||||
let n = 0
|
||||
for (let i = keys.length - 1; i >= 0; i--) {
|
||||
let k = keys[i]
|
||||
if (obj instanceof Array || obj instanceof NodeList) {
|
||||
obj.splice(k, 1)
|
||||
} else {
|
||||
delete obj[k]
|
||||
}
|
||||
n++
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
util.removeArrayItem = function (list, item) {
|
||||
let pos = list.indexOf(item)
|
||||
if (pos !== -1) list.splice(pos, 1)
|
||||
}
|
||||
|
||||
util.last = function (arr) {
|
||||
if (arr && arr.length) {
|
||||
return arr[arr.length - 1]
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
util.len = function (obj) {
|
||||
if (obj instanceof Array || obj instanceof NodeList) {
|
||||
return obj.length
|
||||
} else {
|
||||
let n = 0
|
||||
for (let k in obj) n++
|
||||
return n
|
||||
}
|
||||
}
|
||||
|
||||
util.mergeBy = function (olds, news, ...fields) {
|
||||
if (!olds) return news
|
||||
for (let newItem of news) {
|
||||
let fieldAndValues = []
|
||||
for (let field of fields) {
|
||||
fieldAndValues.push(field, newItem[field])
|
||||
}
|
||||
let oldIndex = util.indexBy(olds, ...fieldAndValues)
|
||||
if (oldIndex === -1) {
|
||||
olds.push(newItem)
|
||||
} else {
|
||||
olds[oldIndex] = newItem
|
||||
}
|
||||
}
|
||||
return olds
|
||||
}
|
||||
|
||||
util.sortBy = function (obj, field, isReverse = false, sortType) {
|
||||
let list = obj instanceof Array || obj instanceof NodeList ? [] : {}
|
||||
let sortedKeys = {}
|
||||
let sortArr = []
|
||||
for (let k in obj) {
|
||||
let v = ''
|
||||
if (field instanceof Array) {
|
||||
for (let f of field) v += obj[k][f]
|
||||
} else {
|
||||
v = obj[k][field]
|
||||
}
|
||||
if (!sortedKeys[v]) {
|
||||
sortedKeys[v] = true
|
||||
sortArr.push(v)
|
||||
}
|
||||
}
|
||||
sortArr.sort((a, b) => {
|
||||
if(sortType === 'int'){
|
||||
a = util.int(a)
|
||||
b = util.int(b)
|
||||
} else if(sortType === 'float'){
|
||||
a = util.float(a)
|
||||
b = util.float(b)
|
||||
}
|
||||
if (a == b) return 0
|
||||
if (typeof a === 'number' && typeof b === 'number') {
|
||||
return isReverse ? b - a : a - b
|
||||
} else {
|
||||
return (isReverse ? a < b : a > b) ? 1 : -1
|
||||
}
|
||||
})
|
||||
for (let sortKey of sortArr) {
|
||||
for (let k in obj) {
|
||||
let v = ''
|
||||
if (field instanceof Array) {
|
||||
for (let f of field) v += obj[k][f]
|
||||
} else {
|
||||
v = obj[k][field]
|
||||
}
|
||||
|
||||
if (obj instanceof Array || obj instanceof NodeList) {
|
||||
if (v == sortKey) list.push(obj[k])
|
||||
} else {
|
||||
if (v == sortKey) list[k] = obj[k]
|
||||
}
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
util.in = function (v1, v2) {
|
||||
if (!(v1 instanceof Array)) v1 = util.split(v1, /,\s*/)
|
||||
return v1.indexOf(String(v2)) !== -1
|
||||
}
|
||||
|
||||
util.uniquePush = function (arr, ...values) {
|
||||
for (let v of values) {
|
||||
if (arr.indexOf(v) === -1) arr.push(v)
|
||||
}
|
||||
}
|
||||
|
||||
util.clearEmpty = function (arr) {
|
||||
let a = []
|
||||
for (let v of arr) if (v) a.push(v)
|
||||
return a
|
||||
}
|
||||
|
||||
util.split = function (v1, v2) {
|
||||
return util.clearEmpty(util.str(v1).split(v2))
|
||||
}
|
||||
|
||||
util.join = function (arr, separator) {
|
||||
return util.clearEmpty(arr).join(separator)
|
||||
}
|
||||
|
||||
util.copy = function (obj, isDeepCopy) {
|
||||
let newObj
|
||||
if (obj instanceof Array || obj instanceof NodeList) {
|
||||
newObj = []
|
||||
for (let o of obj) {
|
||||
if (isDeepCopy && typeof o === 'object' && o) o = util.copy(o)
|
||||
newObj.push(o)
|
||||
}
|
||||
} else {
|
||||
newObj = {}
|
||||
for (let k in obj) {
|
||||
let v = obj[k]
|
||||
if (isDeepCopy && typeof v === 'object' && v) v = util.copy(v)
|
||||
newObj[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
return newObj
|
||||
}
|
||||
|
||||
`,
|
||||
Objects: map[string]interface{}{
|
||||
// makeToken 生成指定长度的随机二进制数组
|
||||
// makeToken size token长度
|
||||
// makeToken return Hex编码的字符串
|
||||
"makeToken": func(size int) string {
|
||||
return hex.EncodeToString(u.MakeToken(size))
|
||||
},
|
||||
|
||||
// makeTokenBytes 生成指定长度的随机二进制数组
|
||||
// makeTokenBytes size token长度
|
||||
// makeTokenBytes return 二进制数据
|
||||
"makeTokenBytes": u.MakeToken,
|
||||
|
||||
// makeId 生成指定长度的随机ID
|
||||
// makeId size ID长度(6~20)
|
||||
// makeId return 二进制数据
|
||||
"makeId": func(size int) string {
|
||||
if size > 20 {
|
||||
return u.UniqueId()
|
||||
} else if size > 14 {
|
||||
return u.UniqueId()[0:size]
|
||||
} else if size > 12 {
|
||||
return u.ShortUniqueId()[0:size]
|
||||
} else if size > 10 {
|
||||
return u.Id12()[0:size]
|
||||
} else if size > 8 {
|
||||
return u.Id10()[0:size]
|
||||
} else if size >= 6 {
|
||||
return u.Id8()[0:size]
|
||||
} else {
|
||||
return u.Id6()
|
||||
}
|
||||
},
|
||||
|
||||
// encodeYaml 将对象转换为YAML格式
|
||||
// encodeYaml content 对象
|
||||
// encodeYaml return YAML字符串
|
||||
"encodeYaml": func(content interface{}) (string, error) {
|
||||
buf, err := yaml.Marshal(content)
|
||||
return string(buf), err
|
||||
},
|
||||
// decodeYaml 将YAML转化为对象
|
||||
// decodeYaml content YAML字符串
|
||||
// decodeYaml return 对象
|
||||
"decodeYaml": func(content string) (interface{}, error) {
|
||||
var data interface{}
|
||||
err := yaml.Unmarshal([]byte(content), &data)
|
||||
return data, err
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue