Files
hunli/hunliji-api/utils/utils.go
2026-08-03 14:27:32 +08:00

174 lines
4.5 KiB
Go

package utils
import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"fmt"
"net/url"
"os"
"path/filepath"
"strings"
"time"
"github.com/gin-gonic/gin"
)
var adminSecret string
// InitAuth 设置后台 token 签名密钥
func InitAuth(secret string) {
adminSecret = secret
}
func GetEnv(k, def string) string {
if v := os.Getenv(k); v != "" {
return v
}
return def
}
func SignToken(account string) string {
payload := fmt.Sprintf(`{"account":%q,"exp":%d}`, account, time.Now().Add(24*time.Hour).Unix())
b64 := base64.StdEncoding.EncodeToString([]byte(payload))
mac := hmac.New(sha256.New, []byte(adminSecret))
mac.Write([]byte(b64))
sig := base64.StdEncoding.EncodeToString(mac.Sum(nil))
return b64 + "." + sig
}
func SignCashGiftViewToken() string {
payload := fmt.Sprintf(`{"account":%q,"exp":%d}`, "cash-gift-view", time.Now().Add(7*24*time.Hour).Unix())
b64 := base64.StdEncoding.EncodeToString([]byte(payload))
mac := hmac.New(sha256.New, []byte(adminSecret))
mac.Write([]byte(b64))
sig := base64.StdEncoding.EncodeToString(mac.Sum(nil))
return b64 + "." + sig
}
func SignCashGiftEntryToken() string {
payload := fmt.Sprintf(`{"account":%q,"exp":%d}`, "cash-gift-entry", time.Now().Add(7*24*time.Hour).Unix())
b64 := base64.StdEncoding.EncodeToString([]byte(payload))
mac := hmac.New(sha256.New, []byte(adminSecret))
mac.Write([]byte(b64))
sig := base64.StdEncoding.EncodeToString(mac.Sum(nil))
return b64 + "." + sig
}
func parseTokenClaims(token string) (account string, exp int64, ok bool) {
parts := strings.Split(token, ".")
if len(parts) != 2 {
return "", 0, false
}
mac := hmac.New(sha256.New, []byte(adminSecret))
mac.Write([]byte(parts[0]))
expected := base64.StdEncoding.EncodeToString(mac.Sum(nil))
if !hmac.Equal([]byte(expected), []byte(parts[1])) {
return "", 0, false
}
raw, err := base64.StdEncoding.DecodeString(parts[0])
if err != nil {
return "", 0, false
}
var claims struct {
Account string `json:"account"`
Exp int64 `json:"exp"`
}
if err := json.Unmarshal(raw, &claims); err != nil {
return "", 0, false
}
if time.Now().Unix() > claims.Exp {
return "", 0, false
}
return claims.Account, claims.Exp, true
}
func VerifyToken(token string) bool {
_, _, ok := parseTokenClaims(token)
return ok
}
func VerifyCashGiftViewToken(token string) bool {
account, _, ok := parseTokenClaims(token)
return ok && account == "cash-gift-view"
}
func VerifyCashGiftEntryToken(token string) bool {
account, _, ok := parseTokenClaims(token)
return ok && account == "cash-gift-entry"
}
func BearerToken(c *gin.Context) string {
return strings.TrimPrefix(c.GetHeader("Authorization"), "Bearer ")
}
func RequireAdmin(c *gin.Context) bool {
if !VerifyToken(BearerToken(c)) {
c.JSON(401, gin.H{"error": "未授权,请先登录后台"})
return false
}
return true
}
func CORSMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT")
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Cash-Gift-Token, X-Cash-Gift-Entry-Token")
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}
c.Next()
}
}
func EscapeMySQLComment(s string) string {
s = strings.ReplaceAll(s, `\`, `\\`)
s = strings.ReplaceAll(s, `'`, `''`)
return s
}
func CleanObjectName(name string) string {
name = filepath.Base(name)
name = strings.ReplaceAll(name, "\\", "_")
name = strings.ReplaceAll(name, "/", "_")
name = strings.TrimSpace(name)
if name == "" || name == "." {
return "upload"
}
return name
}
func BuildObjectKey(folder string, originalName string) string {
folder = strings.Trim(folder, "/ ")
name := CleanObjectName(originalName)
fileName := fmt.Sprintf("%s_%s", time.Now().Format("150405000000000"), name)
day := time.Now().Format("20060102")
if folder == "" {
return day + "/" + fileName
}
return folder + "/" + day + "/" + fileName
}
func EscapedObjectKey(key string) string {
parts := strings.Split(key, "/")
for i, part := range parts {
parts[i] = url.PathEscape(part)
}
return strings.Join(parts, "/")
}
func NormalizeEndpoint(endpoint string, region string) string {
endpoint = strings.TrimSpace(endpoint)
region = strings.TrimSpace(region)
if endpoint == "" && region != "" {
endpoint = "https://oss-" + region + ".aliyuncs.com"
}
if endpoint != "" && !strings.HasPrefix(endpoint, "http://") && !strings.HasPrefix(endpoint, "https://") {
endpoint = "https://" + endpoint
}
return endpoint
}