Files
WebHelper/api/js.go
T
2026-05-27 19:18:10 +08:00

177 lines
3.8 KiB
Go

package api
import (
"fmt"
"strings"
"time"
"github.com/dop251/goja"
)
// JsExecResult JS 执行结果
type JsExecResult struct {
Output string `json:"output"` // 控制台输出
Result string `json:"result"` // 返回值
Error string `json:"error"` // 错误信息
Duration string `json:"duration"` // 执行耗时
Success bool `json:"success"` // 是否成功
}
// consoleLog 存储 console.log 输出
type consoleLogger struct {
logs []string
}
func (c *consoleLogger) log(args ...interface{}) {
c.logs = append(c.logs, fmt.Sprint(args...))
}
func (c *consoleLogger) logFn(call goja.FunctionCall) goja.Value {
var args []interface{}
for _, arg := range call.Arguments {
args = append(args, arg.Export())
}
c.logs = append(c.logs, fmt.Sprint(args...))
return goja.Undefined()
}
// ExecuteJs 执行 JavaScript 代码
func ExecuteJs(code string) *JsExecResult {
result := &JsExecResult{
Success: false,
}
if strings.TrimSpace(code) == "" {
result.Error = "代码不能为空"
return result
}
start := time.Now()
// 创建 JS 虚拟机
vm := goja.New()
// 创建日志记录器
logger := &consoleLogger{
logs: make([]string, 0),
}
// 注入 console 对象
consoleObj := vm.NewObject()
consoleObj.Set("log", logger.logFn)
consoleObj.Set("info", logger.logFn)
consoleObj.Set("warn", logger.logFn)
consoleObj.Set("error", logger.logFn)
vm.Set("console", consoleObj)
// 注入常用全局函数
vm.Set("parseInt", parseInt)
vm.Set("parseFloat", parseFloat)
vm.Set("isNaN", isNaN)
vm.Set("isFinite", isFinite)
vm.Set("String", vm.Get("String"))
vm.Set("Number", vm.Get("Number"))
vm.Set("Boolean", vm.Get("Boolean"))
vm.Set("Array", vm.Get("Array"))
vm.Set("Object", vm.Get("Object"))
vm.Set("JSON", vm.Get("JSON"))
vm.Set("Math", vm.Get("Math"))
vm.Set("Date", vm.Get("Date"))
vm.Set("RegExp", vm.Get("RegExp"))
// 执行代码
val, err := vm.RunString(code)
if err != nil {
result.Error = err.Error()
result.Duration = time.Since(start).String()
result.Output = strings.Join(logger.logs, "\n")
return result
}
// 获取返回值
resultVal := ""
if val != nil && !goja.IsUndefined(val) && !goja.IsNull(val) {
resultVal = fmt.Sprintf("%v", val.Export())
}
result.Output = strings.Join(logger.logs, "\n")
result.Result = resultVal
result.Duration = time.Since(start).String()
result.Success = true
return result
}
// parseInt 解析整数
func parseInt(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 1 {
return goja.NaN()
}
arg := call.Arguments[0].String()
var radix int64 = 10
if len(call.Arguments) > 1 {
radix = call.Arguments[1].ToInteger()
}
var result int64
var err error
switch radix {
case 16:
_, err = fmt.Sscanf(arg, "%x", &result)
case 8:
_, err = fmt.Sscanf(arg, "%o", &result)
default:
_, err = fmt.Sscanf(arg, "%d", &result)
}
if err != nil {
return goja.NaN()
}
return goja.New().ToValue(result)
}
// parseFloat 解析浮点数
func parseFloat(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 1 {
return goja.NaN()
}
arg := call.Arguments[0].String()
var result float64
_, err := fmt.Sscanf(arg, "%f", &result)
if err != nil {
return goja.NaN()
}
return goja.New().ToValue(result)
}
// isNaN 检查是否为 NaN
func isNaN(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 1 {
return goja.New().ToValue(true)
}
val := call.Arguments[0].ToNumber()
if goja.IsNaN(val) {
return goja.New().ToValue(true)
}
return goja.New().ToValue(false)
}
// isFinite 检查是否为有限数
func isFinite(call goja.FunctionCall) goja.Value {
if len(call.Arguments) < 1 {
return goja.New().ToValue(false)
}
val := call.Arguments[0].ToNumber()
if goja.IsNaN(val) || goja.IsInfinity(val) {
return goja.New().ToValue(false)
}
return goja.New().ToValue(true)
}