This commit is contained in:
Star 2024-01-25 16:07:48 +08:00
parent 7fa952faf5
commit 2340fd4fa4
15 changed files with 2819 additions and 23 deletions

25
.gitignore vendored
View File

@ -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

21
crypto/crypt/Config.go Normal file
View File

@ -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 {
}

38
crypto/crypt/Crypt.go Normal file
View File

@ -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
}

30
crypto/crypt/Hash.go Normal file
View File

@ -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)
}

163
crypto/crypt/Sign.go Normal file
View File

@ -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)
//}

426
crypto/crypto.go Normal file
View File

@ -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、ECDSAgm-国密算法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
}
}

302
db/db.go Normal file
View File

@ -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
}

128
discover/discover.go Normal file
View File

@ -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
}

17
file/file-notwindows.go Normal file
View File

@ -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)
}
}

590
file/file.go Normal file
View File

@ -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
}

31
go.mod Normal file
View File

@ -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
)

367
http/http.go Normal file
View File

@ -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()
}

309
redis/redis.go Normal file
View File

@ -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()
}

39
runtime/runtime.go Normal file
View File

@ -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...)
},
},
})
}

356
util/util.go Normal file
View File

@ -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
},
},
})
}