Add files via upload

This commit is contained in:
zhangch-dev
2025-05-06 18:51:53 +08:00
committed by GitHub
parent 0ccc0620f3
commit e7b5ac85e1
52 changed files with 7266 additions and 0 deletions

139
backend/public/cert.go Normal file
View File

@@ -0,0 +1,139 @@
package public
import (
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/hex"
"encoding/pem"
"errors"
"fmt"
"time"
)
// **解析 PEM 格式的证书**
func ParseCertificate(certPEM []byte) (*x509.Certificate, error) {
block, _ := pem.Decode(certPEM)
if block == nil {
return nil, fmt.Errorf("无法解析证书 PEM")
}
return x509.ParseCertificate(block.Bytes)
}
// **解析 PEM 格式的私钥**
func ParsePrivateKey(keyPEM []byte) (crypto.PrivateKey, error) {
block, _ := pem.Decode(keyPEM)
if block == nil {
return nil, fmt.Errorf("无法解析私钥 PEM")
}
// **尝试解析不同类型的私钥**
if key, err := x509.ParsePKCS1PrivateKey(block.Bytes); err == nil {
return key, nil
}
if key, err := x509.ParseECPrivateKey(block.Bytes); err == nil {
return key, nil
}
if key, err := x509.ParsePKCS8PrivateKey(block.Bytes); err == nil {
return key, nil
}
return nil, fmt.Errorf("无法识别的私钥格式")
}
// **检查证书是否过期**
func CheckCertificateExpiration(cert *x509.Certificate) error {
now := time.Now()
if now.Before(cert.NotBefore) {
return fmt.Errorf("证书尚未生效,有效期开始于: %v", cert.NotBefore)
}
if now.After(cert.NotAfter) {
return fmt.Errorf("证书已过期,有效期截止到: %v", cert.NotAfter)
}
return nil
}
// **检查证书是否与私钥匹配**
func VerifyCertificateAndKey(cert *x509.Certificate, privateKey crypto.PrivateKey) error {
messageARR := sha256.Sum256([]byte("test message"))
message := messageARR[:]
var signature []byte
var err error
// **用私钥签名数据**
switch key := privateKey.(type) {
case *rsa.PrivateKey:
signature, err = rsa.SignPKCS1v15(nil, key, crypto.SHA256, message)
case *ecdsa.PrivateKey:
signature, err = key.Sign(nil, message, crypto.SHA256)
case ed25519.PrivateKey:
signature = ed25519.Sign(key, message)
default:
err = errors.New("不支持的私钥类型")
}
if err != nil {
return fmt.Errorf("签名失败: %v", err)
}
// **使用公钥验证签名**
switch pub := cert.PublicKey.(type) {
case *rsa.PublicKey:
err = rsa.VerifyPKCS1v15(pub, crypto.SHA256, message, signature)
case *ecdsa.PublicKey:
ok := ecdsa.VerifyASN1(pub, message, signature)
if !ok {
err = fmt.Errorf("ECDSA 签名验证失败")
}
case ed25519.PublicKey:
if !ed25519.Verify(pub, message, signature) {
err = fmt.Errorf("Ed25519 签名验证失败")
}
default:
err = fmt.Errorf("不支持的公钥类型: %T", pub)
}
return err
}
// **主验证函数**
func ValidateSSLCertificate(certStr, keyStr string) error {
certPEM, keyPEM := []byte(certStr), []byte(keyStr)
// **解析证书和私钥**
cert, err := ParseCertificate(certPEM)
if err != nil {
return fmt.Errorf("解析证书失败: %v", err)
}
privateKey, err := ParsePrivateKey(keyPEM)
if err != nil {
return fmt.Errorf("解析私钥失败: %v", err)
}
// **检查证书有效期**
if err := CheckCertificateExpiration(cert); err != nil {
return err
}
// **检查证书和私钥是否匹配**
if err := VerifyCertificateAndKey(cert, privateKey); err != nil {
return fmt.Errorf("证书与私钥不匹配: %v", err)
}
return nil
}
// 获取sha256
func GetSHA256(certStr string) (string, error) {
certPEM := []byte(certStr)
block, _ := pem.Decode(certPEM)
if block == nil {
return "", fmt.Errorf("无法解析证书 PEM")
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return "", fmt.Errorf("解析证书失败: %v", err)
}
sha256Hash := sha256.Sum256(cert.Raw)
return hex.EncodeToString(sha256Hash[:]), nil
}

44
backend/public/config.go Normal file
View File

@@ -0,0 +1,44 @@
package public
import "strconv"
var Port = GetSettingIgnoreError("port")
var Secure = GetSettingIgnoreError("secure")
var SessionKey = GetSettingIgnoreError("session_key")
var LogPath = GetSettingIgnoreError("log_path")
var TimeOut = func() int {
settingStr := GetSettingIgnoreError("timeout")
setting, err := strconv.Atoi(settingStr)
if err != nil {
return 300
}
return setting
}()
var ShutdownFunc func()
func ReloadConfig() {
Port = GetSettingIgnoreError("port")
Secure = GetSettingIgnoreError("secure")
SessionKey = GetSettingIgnoreError("session_key")
LogPath = GetSettingIgnoreError("log_path")
settingStr := GetSettingIgnoreError("timeout")
setting, err := strconv.Atoi(settingStr)
if err != nil {
TimeOut = 300
} else {
TimeOut = setting
}
ShutdownFunc = nil
}
// OpLog 操作日志
type OpLog struct {
OpType string `db:"op_type"`
OpUser string `db:"op_user"`
OpTime string `db:"op_time"`
OpDetail string `db:"op_detail"`
OpResult string `db:"op_result"`
IP string `db:"ip"`
}

112
backend/public/logger.go Normal file
View File

@@ -0,0 +1,112 @@
package public
import (
"log"
"os"
"path/filepath"
"sync"
"time"
)
var (
infoLogger *log.Logger
errorLogger *log.Logger
warnLogger *log.Logger
logFile *os.File
)
// InitLogger 初始化日志器(仅写入文件)
func InitLogger(logPath string) {
// 确保日志目录存在
dir := filepath.Dir(logPath)
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
panic("创建日志目录失败: " + err.Error())
}
var err error
logFile, err = os.OpenFile(logPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
panic("无法打开日志文件: " + err.Error())
}
infoLogger = log.New(logFile, "[INFO] ", log.LstdFlags|log.Lshortfile)
errorLogger = log.New(logFile, "[ERROR] ", log.LstdFlags|log.Lshortfile)
warnLogger = log.New(logFile, "[WARN] ", log.LstdFlags|log.Lshortfile)
}
// Info 输出 Info 级别日志
func Info(msg string) {
infoLogger.Println(msg)
}
// Error 输出 Error 级别日志
func Error(msg string) {
errorLogger.Println(msg)
}
// Warn 输出 Warn 级别日志
func Warn(msg string) {
warnLogger.Println(msg)
}
// CloseLogger 关闭日志文件(建议在程序退出前调用)
func CloseLogger() {
if logFile != nil {
logFile.Close()
}
}
type Logger struct {
filePath string
logger *log.Logger
file *os.File
mutex sync.Mutex
}
func NewLogger(filePath string) (*Logger, error) {
// 确保日志目录存在
dir := filepath.Dir(filePath)
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
panic("创建日志目录失败: " + err.Error())
}
file, err := os.OpenFile(filePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return nil, err
}
l := log.New(file, "", 0) // 不设置前缀,我们手动格式化
return &Logger{
filePath: filePath,
logger: l,
file: file,
}, nil
}
// Close 关闭日志文件
func (l *Logger) Close() {
l.file.Close()
}
// write 写日志,内部使用锁保证线程安全
func (l *Logger) write(level string, msg string) {
l.mutex.Lock()
defer l.mutex.Unlock()
timestamp := time.Now().Format("2006-01-02 15:04:05")
logLine := "[" + level + "] " + timestamp + " - " + msg
l.logger.Println(logLine)
}
// Info 输出 info 级别日志
func (l *Logger) Info(msg string) {
l.write("INFO", msg)
}
// Error 输出 error 级别日志
func (l *Logger) Error(msg string) {
l.write("ERROR", msg)
}
func (l *Logger) Debug(msg string) {
l.write("Debug", msg)
}

View File

@@ -0,0 +1,44 @@
package public
import "github.com/gin-gonic/gin"
type Response struct {
Code int `json:"code"`
Count int `json:"count"`
Data any `json:"data"`
Message string `json:"message"`
Status bool `json:"status"`
}
func SuccessMsg(c *gin.Context, msg string) {
c.JSON(200, Response{
Code: 200,
Count: 0,
Data: nil,
Message: msg,
Status: true,
})
c.Abort()
}
func SuccessData(c *gin.Context, data interface{}, count int) {
c.JSON(200, Response{
Code: 200,
Count: count,
Data: data,
Message: "success",
Status: true,
})
c.Abort()
}
func FailMsg(c *gin.Context, msg string) {
c.JSON(500, Response{
Code: 500,
Count: 0,
Data: nil,
Message: msg,
Status: false,
})
c.Abort()
}

1094
backend/public/sqlite.go Normal file

File diff suppressed because it is too large Load Diff

181
backend/public/utils.go Normal file
View File

@@ -0,0 +1,181 @@
package public
import (
"crypto/rand"
"fmt"
"github.com/google/uuid"
"io"
"math/big"
"net"
"net/http"
"strings"
)
const defaultCharset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
// GetSettingIgnoreError 获取系统配置-忽略错误
func GetSettingIgnoreError(key string) string {
s, err := NewSqlite("data/data.db", "")
if err != nil {
return ""
}
s.Connect()
defer s.Close()
s.TableName = "settings"
res, err := s.Where("key=?", []interface{}{key}).Select()
if err != nil {
return ""
}
if len(res) == 0 {
return ""
}
setting, ok := res[0]["value"].(string)
if !ok {
return ""
}
return setting
}
func UpdateSetting(key, val string) error {
s, err := NewSqlite("data/data.db", "")
if err != nil {
return err
}
s.Connect()
defer s.Close()
s.TableName = "settings"
_, err = s.Where("key=?", []interface{}{key}).Update(map[string]any{"value": val})
if err != nil {
return err
}
return nil
}
func GetSettingsFromType(typ string) ([]map[string]any, error) {
db := "data/data.db"
s, err := NewSqlite(db, "")
if err != nil {
return nil, err
}
s.Connect()
defer s.Close()
s.TableName = "settings"
res, err := s.Where("type=?", []interface{}{typ}).Select()
if err != nil {
return nil, err
}
return res, nil
}
// GetFreePort 获取一个可用的随机端口
func GetFreePort() (int, error) {
// 端口为 0表示让系统自动分配一个可用端口
ln, err := net.Listen("tcp", "localhost:0")
if err != nil {
return 0, err
}
defer ln.Close()
addr := ln.Addr().String()
// 提取端口号
parts := strings.Split(addr, ":")
if len(parts) < 2 {
return 0, fmt.Errorf("invalid address: %s", addr)
}
var port int
fmt.Sscanf(parts[len(parts)-1], "%d", &port)
return port, nil
}
// RandomString 生成指定长度的随机字符串
func RandomString(length int) string {
if str, err := RandomStringWithCharset(length, defaultCharset); err != nil {
return "allinssl"
} else {
return str
}
}
// RandomStringWithCharset 使用指定字符集生成随机字符串
func RandomStringWithCharset(length int, charset string) (string, error) {
result := make([]byte, length)
charsetLen := big.NewInt(int64(len(charset)))
for i := 0; i < length; i++ {
num, err := rand.Int(rand.Reader, charsetLen)
if err != nil {
return "", err
}
result[i] = charset[num.Int64()]
}
return string(result), nil
}
// GenerateUUID 生成 UUID
func GenerateUUID() string {
// 生成一个新的 UUID
uuidStr := strings.ReplaceAll(uuid.New().String(), "-", "")
// 返回 UUID 的字符串表示
return uuidStr
}
func GetLocalIP() (string, error) {
interfaces, err := net.Interfaces()
if err != nil {
return "", err
}
for _, iface := range interfaces {
if iface.Flags&net.FlagUp == 0 {
continue // 接口未启用
}
if iface.Flags&net.FlagLoopback != 0 {
continue // 忽略回环地址
}
addrs, err := iface.Addrs()
if err != nil {
continue
}
for _, addr := range addrs {
var ip net.IP
switch v := addr.(type) {
case *net.IPNet:
ip = v.IP
case *net.IPAddr:
ip = v.IP
}
// 只返回 IPv4 内网地址
if ip != nil && ip.To4() != nil && !ip.IsLoopback() {
return ip.String(), nil
}
}
}
return "", fmt.Errorf("没有找到内网 IP")
}
func GetPublicIP() (string, error) {
resp, err := http.Get("https://www.bt.cn/Api/getIpAddress")
if err != nil {
return "", fmt.Errorf("请求失败: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("HTTP状态错误: %v", resp.Status)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("读取响应失败: %v", err)
}
return string(body), nil
}

View File

@@ -0,0 +1,35 @@
package public
import (
"github.com/mojocn/base64Captcha"
"image/color"
)
var codeDefaultDriver = base64Captcha.NewDriverString(
1000,
1200,
0,
base64Captcha.OptionShowSlimeLine,
4,
"23456789abcdefghjkmnpqrstuvwxyz",
&color.RGBA{
R: 225,
G: 225,
B: 200,
A: 255,
},
nil,
[]string{"wqy-microhei.ttc", "RitaSmith.ttf"},
)
// GenerateCode 生成图形化字符串验证码
func GenerateCode() (string, string, string, error) {
// 生成默认数字的driver
codeId, content, _ := codeDefaultDriver.GenerateIdQuestionAnswer() // 生成验证码和随机id
item, err := codeDefaultDriver.DrawCaptcha(content) // 生成验证码图片
if err != nil {
return "", "", "", err
}
b64s := item.EncodeB64string()
return codeId, b64s, content, nil
}