mirror of
https://github.com/QuantumNous/new-api.git
synced 2026-09-07 01:56:53 +00:00
140 lines
4.6 KiB
Go
140 lines
4.6 KiB
Go
package jsplugin
|
|
|
|
import (
|
|
"crypto/hmac"
|
|
"crypto/sha256"
|
|
"encoding/base64"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"net/url"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/golang-jwt/jwt/v5"
|
|
"github.com/google/uuid"
|
|
"github.com/grafana/sobek"
|
|
)
|
|
|
|
type volcSignRequest struct {
|
|
Method string `json:"method"`
|
|
URL string `json:"url"`
|
|
Headers map[string]string `json:"headers"`
|
|
Body string `json:"body"`
|
|
AccessKey string `json:"accessKey"`
|
|
SecretKey string `json:"secretKey"`
|
|
Region string `json:"region"`
|
|
Service string `json:"service"`
|
|
Timestamp int64 `json:"timestamp"`
|
|
}
|
|
|
|
func injectGlobals(runtime *sobek.Runtime, identity func() string, now func() time.Time, logOutput func(string)) error {
|
|
utils := map[string]any{
|
|
"unixNow": func() int64 { return now().Unix() },
|
|
"jwtSignHS256": func(claims map[string]any, secret string) (string, error) {
|
|
return jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims(claims)).SignedString([]byte(secret))
|
|
},
|
|
"hmacSHA256": func(message, secret string) string {
|
|
mac := hmac.New(sha256.New, []byte(secret))
|
|
_, _ = mac.Write([]byte(message))
|
|
return hex.EncodeToString(mac.Sum(nil))
|
|
},
|
|
"base64": func(value string) string { return base64.StdEncoding.EncodeToString([]byte(value)) },
|
|
"base64URL": func(value string) string {
|
|
return base64.RawURLEncoding.EncodeToString([]byte(value))
|
|
},
|
|
"base64URLDecode": func(value string) (string, error) {
|
|
decoded, err := base64.RawURLEncoding.DecodeString(value)
|
|
return string(decoded), err
|
|
},
|
|
"uuid": func() string { return uuid.NewString() },
|
|
"volcSignV4": func(request volcSignRequest) (map[string]string, error) {
|
|
return signVolcV4(request, now)
|
|
},
|
|
}
|
|
if err := runtime.Set("utils", utils); err != nil {
|
|
return err
|
|
}
|
|
console := runtime.NewObject()
|
|
if err := console.Set("log", func(call sobek.FunctionCall) sobek.Value {
|
|
parts := make([]string, len(call.Arguments))
|
|
for i, argument := range call.Arguments {
|
|
parts[i] = argument.String()
|
|
}
|
|
if logOutput != nil {
|
|
logOutput(identity() + " " + strings.Join(parts, " "))
|
|
}
|
|
return sobek.Undefined()
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
return runtime.Set("console", console)
|
|
}
|
|
|
|
func signVolcV4(request volcSignRequest, now func() time.Time) (map[string]string, error) {
|
|
parsedURL, err := url.Parse(request.URL)
|
|
if err != nil || parsedURL.Host == "" {
|
|
return nil, fmt.Errorf("invalid Volcengine signing URL")
|
|
}
|
|
region := request.Region
|
|
if region == "" {
|
|
region = "cn-north-1"
|
|
}
|
|
service := request.Service
|
|
if service == "" {
|
|
service = "cv"
|
|
}
|
|
timestamp := now().UTC()
|
|
if request.Timestamp != 0 {
|
|
timestamp = time.Unix(request.Timestamp, 0).UTC()
|
|
}
|
|
xDate := timestamp.Format("20060102T150405Z")
|
|
shortDate := timestamp.Format("20060102")
|
|
bodyHash := sha256.Sum256([]byte(request.Body))
|
|
requestPath := parsedURL.EscapedPath()
|
|
if requestPath == "" {
|
|
requestPath = "/"
|
|
}
|
|
|
|
headers := make(map[string]string, len(request.Headers)+3)
|
|
for name, value := range request.Headers {
|
|
headers[strings.ToLower(name)] = strings.TrimSpace(value)
|
|
}
|
|
headers["host"] = parsedURL.Host
|
|
headers["x-date"] = xDate
|
|
headers["x-content-sha256"] = hex.EncodeToString(bodyHash[:])
|
|
keys := make([]string, 0, len(headers))
|
|
for name := range headers {
|
|
keys = append(keys, name)
|
|
}
|
|
sort.Strings(keys)
|
|
var canonicalHeaders strings.Builder
|
|
for _, name := range keys {
|
|
canonicalHeaders.WriteString(name)
|
|
canonicalHeaders.WriteByte(':')
|
|
canonicalHeaders.WriteString(headers[name])
|
|
canonicalHeaders.WriteByte('\n')
|
|
}
|
|
signedHeaders := strings.Join(keys, ";")
|
|
canonicalRequest := strings.Join([]string{
|
|
strings.ToUpper(request.Method), requestPath, parsedURL.Query().Encode(),
|
|
canonicalHeaders.String(), signedHeaders, hex.EncodeToString(bodyHash[:]),
|
|
}, "\n")
|
|
canonicalHash := sha256.Sum256([]byte(canonicalRequest))
|
|
scope := fmt.Sprintf("%s/%s/%s/request", shortDate, region, service)
|
|
stringToSign := fmt.Sprintf("HMAC-SHA256\n%s\n%s\n%s", xDate, scope, hex.EncodeToString(canonicalHash[:]))
|
|
sign := func(key []byte, value string) []byte {
|
|
mac := hmac.New(sha256.New, key)
|
|
_, _ = mac.Write([]byte(value))
|
|
return mac.Sum(nil)
|
|
}
|
|
signingKey := sign(sign(sign([]byte(request.SecretKey), shortDate), region), service)
|
|
signingKey = sign(signingKey, "request")
|
|
signature := hex.EncodeToString(sign(signingKey, stringToSign))
|
|
return map[string]string{
|
|
"Authorization": fmt.Sprintf("HMAC-SHA256 Credential=%s/%s, SignedHeaders=%s, Signature=%s", request.AccessKey, scope, signedHeaders, signature),
|
|
"X-Date": xDate,
|
|
"X-Content-Sha256": hex.EncodeToString(bodyHash[:]),
|
|
}, nil
|
|
}
|