mirror of
https://github.com/QuantumNous/new-api.git
synced 2026-09-07 01:56:53 +00:00
feat(task): replace built-in task adaptors with a sandboxed JS plugin system (#7076)
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user