1、平台多ApiKey支持
2、平台号码跟系统号码对比自动续费
This commit is contained in:
221
utils/utility/crypto_helper.go
Normal file
221
utils/utility/crypto_helper.go
Normal file
@ -0,0 +1,221 @@
|
||||
package utility
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// 加密key
|
||||
var CryptoKey = "ProxyServer@#(123321)!Keycrypto"
|
||||
|
||||
// CryptoHelper 加密帮助类
|
||||
type CryptoHelper struct {
|
||||
key []byte
|
||||
}
|
||||
|
||||
// NewCryptoHelper 创建新的加密帮助实例
|
||||
// key: 32字节的加密密钥,如果长度不足会自动填充,超出会截断
|
||||
func NewCryptoHelper(key string) *CryptoHelper {
|
||||
// 确保密钥长度为32字节(AES-256)
|
||||
keyBytes := make([]byte, 32)
|
||||
copy(keyBytes, []byte(key))
|
||||
return &CryptoHelper{
|
||||
key: keyBytes,
|
||||
}
|
||||
}
|
||||
|
||||
// Encrypt 加密字符串
|
||||
// plaintext: 要加密的明文
|
||||
// 返回: base64编码的密文和错误信息
|
||||
func (c *CryptoHelper) Encrypt(plaintext string) (string, error) {
|
||||
if plaintext == "" {
|
||||
return "", errors.New("plaintext cannot be empty")
|
||||
}
|
||||
|
||||
// 创建AES cipher
|
||||
block, err := aes.NewCipher(c.key)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create cipher: %w", err)
|
||||
}
|
||||
|
||||
// 使用GCM模式
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create GCM: %w", err)
|
||||
}
|
||||
|
||||
// 生成随机nonce
|
||||
nonce := make([]byte, gcm.NonceSize())
|
||||
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
|
||||
return "", fmt.Errorf("failed to generate nonce: %w", err)
|
||||
}
|
||||
|
||||
// 加密数据
|
||||
ciphertext := gcm.Seal(nonce, nonce, []byte(plaintext), nil)
|
||||
|
||||
// 返回base64编码的结果
|
||||
return base64.StdEncoding.EncodeToString(ciphertext), nil
|
||||
}
|
||||
|
||||
// Decrypt 解密字符串
|
||||
// ciphertext: base64编码的密文
|
||||
// 返回: 解密后的明文和错误信息
|
||||
func (c *CryptoHelper) Decrypt(ciphertext string) (string, error) {
|
||||
if ciphertext == "" {
|
||||
return "", errors.New("ciphertext cannot be empty")
|
||||
}
|
||||
|
||||
// base64解码
|
||||
data, err := base64.StdEncoding.DecodeString(ciphertext)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to decode base64: %w", err)
|
||||
}
|
||||
|
||||
// 创建AES cipher
|
||||
block, err := aes.NewCipher(c.key)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create cipher: %w", err)
|
||||
}
|
||||
|
||||
// 使用GCM模式
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create GCM: %w", err)
|
||||
}
|
||||
|
||||
// 检查数据长度
|
||||
nonceSize := gcm.NonceSize()
|
||||
if len(data) < nonceSize {
|
||||
return "", errors.New("ciphertext too short")
|
||||
}
|
||||
|
||||
// 提取nonce和密文
|
||||
nonce, cipherData := data[:nonceSize], data[nonceSize:]
|
||||
|
||||
// 解密数据
|
||||
plaintext, err := gcm.Open(nil, nonce, cipherData, nil)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to decrypt: %w", err)
|
||||
}
|
||||
|
||||
return string(plaintext), nil
|
||||
}
|
||||
|
||||
// EncryptBytes 加密字节数组
|
||||
// data: 要加密的字节数组
|
||||
// 返回: 加密后的字节数组和错误信息
|
||||
func (c *CryptoHelper) EncryptBytes(data []byte) ([]byte, error) {
|
||||
if len(data) == 0 {
|
||||
return nil, errors.New("data cannot be empty")
|
||||
}
|
||||
|
||||
// 创建AES cipher
|
||||
block, err := aes.NewCipher(c.key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create cipher: %w", err)
|
||||
}
|
||||
|
||||
// 使用GCM模式
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create GCM: %w", err)
|
||||
}
|
||||
|
||||
// 生成随机nonce
|
||||
nonce := make([]byte, gcm.NonceSize())
|
||||
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
|
||||
return nil, fmt.Errorf("failed to generate nonce: %w", err)
|
||||
}
|
||||
|
||||
// 加密数据
|
||||
ciphertext := gcm.Seal(nonce, nonce, data, nil)
|
||||
|
||||
return ciphertext, nil
|
||||
}
|
||||
|
||||
// DecryptBytes 解密字节数组
|
||||
// ciphertext: 加密后的字节数组
|
||||
// 返回: 解密后的字节数组和错误信息
|
||||
func (c *CryptoHelper) DecryptBytes(ciphertext []byte) ([]byte, error) {
|
||||
if len(ciphertext) == 0 {
|
||||
return nil, errors.New("ciphertext cannot be empty")
|
||||
}
|
||||
|
||||
// 创建AES cipher
|
||||
block, err := aes.NewCipher(c.key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create cipher: %w", err)
|
||||
}
|
||||
|
||||
// 使用GCM模式
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create GCM: %w", err)
|
||||
}
|
||||
|
||||
// 检查数据长度
|
||||
nonceSize := gcm.NonceSize()
|
||||
if len(ciphertext) < nonceSize {
|
||||
return nil, errors.New("ciphertext too short")
|
||||
}
|
||||
|
||||
// 提取nonce和密文
|
||||
nonce, cipherData := ciphertext[:nonceSize], ciphertext[nonceSize:]
|
||||
|
||||
// 解密数据
|
||||
plaintext, err := gcm.Open(nil, nonce, cipherData, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decrypt: %w", err)
|
||||
}
|
||||
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
// GenerateKey 生成随机密钥
|
||||
// 返回: 32字节的随机密钥字符串
|
||||
func GenerateKey() (string, error) {
|
||||
key := make([]byte, 32)
|
||||
if _, err := io.ReadFull(rand.Reader, key); err != nil {
|
||||
return "", fmt.Errorf("failed to generate key: %w", err)
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString(key), nil
|
||||
}
|
||||
|
||||
// QuickEncrypt 快速加密函数(使用默认密钥)
|
||||
// plaintext: 要加密的明文
|
||||
// key: 加密密钥
|
||||
// 返回: base64编码的密文和错误信息
|
||||
func QuickEncrypt(plaintext, key string) (string, error) {
|
||||
crypto := NewCryptoHelper(key)
|
||||
return crypto.Encrypt(plaintext)
|
||||
}
|
||||
|
||||
// QuickDecrypt2 快速解密函数(使用默认密钥)
|
||||
// ciphertext: base64编码的密文
|
||||
// 返回: 解密后的明文和错误信息
|
||||
func QuickDecrypt2(ciphertext string) (string, error) {
|
||||
crypto := NewCryptoHelper(CryptoKey)
|
||||
return crypto.Decrypt(ciphertext)
|
||||
}
|
||||
|
||||
// QuickEncrypt2 快速加密函数(使用默认密钥)
|
||||
// plaintext: 要加密的明文
|
||||
// 返回: base64编码的密文和错误信息
|
||||
func QuickEncrypt2(plaintext string) (string, error) {
|
||||
crypto := NewCryptoHelper(CryptoKey)
|
||||
return crypto.Encrypt(plaintext)
|
||||
}
|
||||
|
||||
// QuickDecrypt 快速解密函数(使用默认密钥)
|
||||
// ciphertext: base64编码的密文
|
||||
// key: 解密密钥
|
||||
// 返回: 解密后的明文和错误信息
|
||||
func QuickDecrypt(ciphertext, key string) (string, error) {
|
||||
crypto := NewCryptoHelper(key)
|
||||
return crypto.Decrypt(ciphertext)
|
||||
}
|
||||
371
utils/utility/generic_queue.go
Normal file
371
utils/utility/generic_queue.go
Normal file
@ -0,0 +1,371 @@
|
||||
package utility
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// GenericQueue 通用循环队列
|
||||
// 支持存储任意类型的数据,实现负载均衡和容错
|
||||
type GenericQueue[T any] struct {
|
||||
data []T // 存储数据的数组
|
||||
current int // 当前指针位置
|
||||
size int // 队列中的元素数量
|
||||
capacity int // 队列容量
|
||||
mutex sync.RWMutex // 读写锁,保证线程安全
|
||||
lastUsed map[int]time.Time // 记录每个位置的最后使用时间
|
||||
cooldown time.Duration // 冷却时间,避免频繁使用同一个元素
|
||||
comparer func(T, T) bool // 比较函数,用于检查重复元素
|
||||
}
|
||||
|
||||
var (
|
||||
// QuequeMap 全局队列映射表,用于管理多个命名队列
|
||||
// 使用interface{}类型以支持不同泛型类型的队列
|
||||
QuequeMap = make(map[string]interface{})
|
||||
)
|
||||
|
||||
// NewGenericQueue 创建新的通用循环队列
|
||||
// capacity: 队列容量
|
||||
// comparer: 比较函数,用于检查重复元素(可选)
|
||||
// cooldown: 元素使用冷却时间(可选,默认0表示无冷却)
|
||||
func NewGenericQueue[T any](capacity int, comparer func(T, T) bool, cooldown ...time.Duration) *GenericQueue[T] {
|
||||
cd := time.Duration(0)
|
||||
if len(cooldown) > 0 {
|
||||
cd = cooldown[0]
|
||||
}
|
||||
|
||||
return &GenericQueue[T]{
|
||||
data: make([]T, capacity),
|
||||
capacity: capacity,
|
||||
lastUsed: make(map[int]time.Time),
|
||||
cooldown: cd,
|
||||
comparer: comparer,
|
||||
}
|
||||
}
|
||||
|
||||
// Add 添加元素到队列
|
||||
// item: 要添加的元素
|
||||
// 返回: 是否添加成功
|
||||
func (q *GenericQueue[T]) Add(item T) bool {
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
if q.size >= q.capacity {
|
||||
return false // 队列已满
|
||||
}
|
||||
|
||||
// 如果提供了比较函数,检查是否已存在相同的元素
|
||||
if q.comparer != nil {
|
||||
for i := 0; i < q.size; i++ {
|
||||
if q.comparer(q.data[i], item) {
|
||||
return false // 已存在
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
q.data[q.size] = item
|
||||
q.size++
|
||||
return true
|
||||
}
|
||||
|
||||
// Remove 从队列中移除指定的元素
|
||||
// item: 要移除的元素
|
||||
// 返回: 是否移除成功
|
||||
func (q *GenericQueue[T]) Remove(item T) bool {
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
if q.comparer == nil {
|
||||
return false // 没有比较函数无法移除
|
||||
}
|
||||
|
||||
for i := 0; i < q.size; i++ {
|
||||
if q.comparer(q.data[i], item) {
|
||||
// 将后面的元素前移
|
||||
for j := i; j < q.size-1; j++ {
|
||||
q.data[j] = q.data[j+1]
|
||||
}
|
||||
q.size--
|
||||
|
||||
// 调整current指针
|
||||
if q.current >= q.size && q.size > 0 {
|
||||
q.current = 0
|
||||
}
|
||||
|
||||
// 清理lastUsed记录
|
||||
delete(q.lastUsed, i)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetNext 获取下一个可用的元素(轮询方式)
|
||||
// 返回: 元素和是否获取成功
|
||||
func (q *GenericQueue[T]) GetNext() (T, bool) {
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
var zero T
|
||||
if q.size == 0 {
|
||||
return zero, false // 队列为空
|
||||
}
|
||||
|
||||
// 如果没有设置冷却时间,直接返回下一个元素
|
||||
if q.cooldown == 0 {
|
||||
item := q.data[q.current]
|
||||
q.lastUsed[q.current] = time.Now()
|
||||
q.current = (q.current + 1) % q.size
|
||||
return item, true
|
||||
}
|
||||
|
||||
// 寻找可用的元素(考虑冷却时间)
|
||||
startPos := q.current
|
||||
for {
|
||||
lastUsed, exists := q.lastUsed[q.current]
|
||||
|
||||
// 如果元素从未使用过,或者已过冷却时间
|
||||
if !exists || time.Since(lastUsed) >= q.cooldown {
|
||||
item := q.data[q.current]
|
||||
q.lastUsed[q.current] = time.Now()
|
||||
q.current = (q.current + 1) % q.size
|
||||
return item, true
|
||||
}
|
||||
|
||||
q.current = (q.current + 1) % q.size
|
||||
|
||||
// 如果遍历了一圈都没找到可用的元素
|
||||
if q.current == startPos {
|
||||
// 返回当前元素,忽略冷却时间
|
||||
item := q.data[q.current]
|
||||
q.lastUsed[q.current] = time.Now()
|
||||
q.current = (q.current + 1) % q.size
|
||||
return item, true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetRandom 随机获取一个元素
|
||||
// 返回: 元素和是否获取成功
|
||||
func (q *GenericQueue[T]) GetRandom() (T, bool) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
||||
var zero T
|
||||
if q.size == 0 {
|
||||
return zero, false
|
||||
}
|
||||
|
||||
// 使用当前时间作为随机种子
|
||||
index := int(time.Now().UnixNano()) % q.size
|
||||
item := q.data[index]
|
||||
q.lastUsed[index] = time.Now()
|
||||
return item, true
|
||||
}
|
||||
|
||||
// GetAll 获取所有元素的副本
|
||||
// 返回: 元素切片
|
||||
func (q *GenericQueue[T]) GetAll() []T {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
||||
items := make([]T, q.size)
|
||||
copy(items, q.data[:q.size])
|
||||
return items
|
||||
}
|
||||
|
||||
// Size 获取队列中的元素数量
|
||||
// 返回: 元素数量
|
||||
func (q *GenericQueue[T]) Size() int {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
return q.size
|
||||
}
|
||||
|
||||
// IsEmpty 检查队列是否为空
|
||||
// 返回: 是否为空
|
||||
func (q *GenericQueue[T]) IsEmpty() bool {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
return q.size == 0
|
||||
}
|
||||
|
||||
// IsFull 检查队列是否已满
|
||||
// 返回: 是否已满
|
||||
func (q *GenericQueue[T]) IsFull() bool {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
return q.size >= q.capacity
|
||||
}
|
||||
|
||||
// Clear 清空队列
|
||||
func (q *GenericQueue[T]) Clear() {
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
q.size = 0
|
||||
q.current = 0
|
||||
q.lastUsed = make(map[int]time.Time)
|
||||
}
|
||||
|
||||
// SetCooldown 设置元素使用冷却时间
|
||||
// cooldown: 冷却时间
|
||||
func (q *GenericQueue[T]) SetCooldown(cooldown time.Duration) {
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
q.cooldown = cooldown
|
||||
}
|
||||
|
||||
// GetUsageInfo 获取元素使用信息
|
||||
// 返回: 位置使用时间映射
|
||||
func (q *GenericQueue[T]) GetUsageInfo() map[int]time.Time {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
||||
usage := make(map[int]time.Time)
|
||||
for k, v := range q.lastUsed {
|
||||
usage[k] = v
|
||||
}
|
||||
return usage
|
||||
}
|
||||
|
||||
// BatchAdd 批量添加元素
|
||||
// items: 要添加的元素切片
|
||||
// 返回: 成功添加的数量
|
||||
func (q *GenericQueue[T]) BatchAdd(items []T) int {
|
||||
count := 0
|
||||
for _, item := range items {
|
||||
if q.Add(item) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
// Replace 替换所有元素
|
||||
// items: 新的元素切片
|
||||
// 返回: 是否替换成功
|
||||
func (q *GenericQueue[T]) Replace(items []T) bool {
|
||||
if len(items) > q.capacity {
|
||||
return false
|
||||
}
|
||||
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
q.size = 0
|
||||
q.current = 0
|
||||
q.lastUsed = make(map[int]time.Time)
|
||||
|
||||
for _, item := range items {
|
||||
q.data[q.size] = item
|
||||
q.size++
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// ReplaceItem 替换指定的单个元素
|
||||
// oldItem: 要被替换的元素
|
||||
// newItem: 新的元素
|
||||
// 返回: 是否替换成功
|
||||
func (q *GenericQueue[T]) ReplaceItem(oldItem, newItem T) bool {
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
if q.comparer == nil {
|
||||
return false // 没有比较函数无法查找元素
|
||||
}
|
||||
|
||||
for i := 0; i < q.size; i++ {
|
||||
if q.comparer(q.data[i], oldItem) {
|
||||
q.data[i] = newItem
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false // 未找到要替换的元素
|
||||
}
|
||||
|
||||
// Enqueue 入队操作(队列尾部添加元素)
|
||||
// item: 要添加的元素
|
||||
// 返回: 是否添加成功
|
||||
func (q *GenericQueue[T]) Enqueue(item T) bool {
|
||||
return q.Add(item)
|
||||
}
|
||||
|
||||
// Dequeue 出队操作(从队列头部移除并返回元素)
|
||||
// 返回: 元素和是否成功
|
||||
func (q *GenericQueue[T]) Dequeue() (T, bool) {
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
var zero T
|
||||
if q.size == 0 {
|
||||
return zero, false // 队列为空
|
||||
}
|
||||
|
||||
// 获取队列头部元素
|
||||
item := q.data[0]
|
||||
|
||||
// 将后面的元素前移
|
||||
for i := 0; i < q.size-1; i++ {
|
||||
q.data[i] = q.data[i+1]
|
||||
}
|
||||
q.size--
|
||||
|
||||
// 调整current指针
|
||||
if q.current > 0 {
|
||||
q.current--
|
||||
}
|
||||
if q.current >= q.size && q.size > 0 {
|
||||
q.current = 0
|
||||
}
|
||||
|
||||
// 重新映射lastUsed(因为索引发生了变化)
|
||||
newLastUsed := make(map[int]time.Time)
|
||||
for index, lastTime := range q.lastUsed {
|
||||
if index > 0 {
|
||||
newLastUsed[index-1] = lastTime
|
||||
}
|
||||
}
|
||||
q.lastUsed = newLastUsed
|
||||
|
||||
return item, true
|
||||
}
|
||||
|
||||
// Peek 查看队列头部元素(不移除)
|
||||
// 返回: 元素和是否成功
|
||||
func (q *GenericQueue[T]) Peek() (T, bool) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
||||
var zero T
|
||||
if q.size == 0 {
|
||||
return zero, false // 队列为空
|
||||
}
|
||||
|
||||
return q.data[0], true
|
||||
}
|
||||
|
||||
// PeekLast 查看队列尾部元素(不移除)
|
||||
// 返回: 元素和是否成功
|
||||
func (q *GenericQueue[T]) PeekLast() (T, bool) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
||||
var zero T
|
||||
if q.size == 0 {
|
||||
return zero, false // 队列为空
|
||||
}
|
||||
|
||||
return q.data[q.size-1], true
|
||||
}
|
||||
|
||||
// ApiKeyInfo API密钥信息结构体
|
||||
type ApiKeyInfo struct {
|
||||
Key string `json:"key"` // API密钥
|
||||
Name string `json:"name"` // 密钥名称
|
||||
Weight int `json:"weight"` // 权重
|
||||
Enabled bool `json:"enabled"` // 是否启用
|
||||
Metadata map[string]string `json:"metadata"` // 元数据
|
||||
}
|
||||
Reference in New Issue
Block a user