From 627c05fd0602ac29e226563dc194d209c66032b9 Mon Sep 17 00:00:00 2001 From: Star Date: Fri, 12 Dec 2025 21:20:57 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8D=87=E7=BA=A7=E5=BC=82=E5=B8=B8=E5=A4=84?= =?UTF-8?q?=E7=90=86=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- caller.go | 5 +- go.mod | 4 +- request.go | 15 +- service.go | 292 +++++++++++++++++++++++++++++++-------- service.ts | 9 +- tests/service_ws_test.go | 2 + tests/task.js | 37 +++-- tests/tpl.js | 7 +- tests/tpl/header.html | 2 +- tests/tpl/page.html | 12 +- ws.go | 33 +++-- 11 files changed, 308 insertions(+), 110 deletions(-) diff --git a/caller.go b/caller.go index 1c39c7f..b66f463 100644 --- a/caller.go +++ b/caller.go @@ -22,7 +22,10 @@ func NewCaller(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { func makeResult(r *httpclient.Result, vm *goja.Runtime) goja.Value { if r.Error != nil { - panic(vm.NewGoError(r.Error)) + // panic(vm.NewGoError(r.Error)) + vm.SetData("_lastError", r.Error) + gojs.GetLogger(vm).Error(r.Error.Error()) + return nil } headers := map[string]string{} for k, v := range r.Response.Header { diff --git a/go.mod b/go.mod index cb462e5..0f8c4e8 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module apigo.cc/gojs/service go 1.24.0 require ( - apigo.cc/gojs v0.0.31 + apigo.cc/gojs v0.0.32 apigo.cc/gojs/console v0.0.4 apigo.cc/gojs/file v0.0.7 apigo.cc/gojs/http v0.0.8 @@ -14,7 +14,7 @@ require ( github.com/ssgo/config v1.7.10 github.com/ssgo/discover v1.7.10 github.com/ssgo/httpclient v1.7.8 - github.com/ssgo/log v1.7.9 + github.com/ssgo/log v1.7.10 github.com/ssgo/redis v1.7.8 github.com/ssgo/s v1.7.25 github.com/ssgo/standard v1.7.7 diff --git a/request.go b/request.go index 92c5634..67bf402 100644 --- a/request.go +++ b/request.go @@ -91,7 +91,10 @@ func (r *Request) MakeURL(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value func (r *Request) ReadAll(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { data, err := io.ReadAll(r.req.Body) if err != nil { - panic(vm.NewGoError(err)) + // panic(vm.NewGoError(err)) + vm.SetData("_lastError", err) + gojs.GetLogger(vm).Error(err.Error()) + return nil } return vm.ToValue(data) } @@ -102,7 +105,10 @@ func (r *Request) Read(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { data := make([]byte, size) n, err := r.req.Body.Read(data) if err != nil { - panic(vm.NewGoError(err)) + // panic(vm.NewGoError(err)) + vm.SetData("_lastError", err) + gojs.GetLogger(vm).Error(err.Error()) + return nil } return vm.ToValue(data[0:n]) } @@ -110,7 +116,10 @@ func (r *Request) Read(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { func (r *Request) Close(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { err := r.req.Body.Close() if err != nil { - panic(vm.NewGoError(err)) + // panic(vm.NewGoError(err)) + vm.SetData("_lastError", err) + gojs.GetLogger(vm).Error(err.Error()) + return nil } return nil } diff --git a/service.go b/service.go index 680e3ee..76b116d 100644 --- a/service.go +++ b/service.go @@ -50,10 +50,11 @@ type TplCache struct { Tpl *template.Template } -var tplFunc = map[string]any{} -var tplFuncLock = sync.RWMutex{} -var tplCache = map[string]*TplCache{} -var tplCacheLock = sync.RWMutex{} +// var tplFunc = map[string]any{} +var tplReplaces = map[string]string{} +var tplLock = sync.RWMutex{} + +// var tplCache = map[string]*TplCache{} type LimiterConfig struct { From string @@ -74,6 +75,7 @@ type Config struct { Limiters map[string]*LimiterConfig LimiterRedis string HotLoad int + TplSafePaths []string Proxy map[string]string Rewrite map[string]string @@ -93,9 +95,12 @@ func initConfig(opt *gojs.Obj, logger *log.Logger, vm *goja.Runtime) { s.SetWorkPath(u.String(startPath)) } // 处理配置 - serviceConfig = Config{"Session", "Device", "Client", "id", "", 3600, "auth failed", "verify failed", "too many requests", nil, "", 0, map[string]string{}, map[string]string{}, map[string]string{}} + serviceConfig = Config{"Session", "Device", "Client", "id", "", 3600, "auth failed", "verify failed", "too many requests", nil, "", 0, []string{}, map[string]string{}, map[string]string{}, map[string]string{}} if errs := config.LoadConfig("service", &serviceConfig); len(errs) > 0 { - panic(vm.NewGoError(errs[0])) + // panic(vm.NewGoError(errs[0])) + vm.SetData("_lastError", errs[0]) + gojs.GetLogger(vm).Error(errs[0].Error()) + return } config.LoadConfig("service", &discover.Config) // var auth goja.Callable @@ -138,6 +143,7 @@ func initConfig(opt *gojs.Obj, logger *log.Logger, vm *goja.Runtime) { } } } + // 如果没有session中的authLevel验证失败,则使用Access-Token中的authLevel(服务间调用) if authAccessToken && setAuthLevel < authLevel { setAuthLevel = s.GetAuthTokenLevel(request.Header.Get("Access-Token")) @@ -251,7 +257,10 @@ func init() { // panic(vm.NewGoError(errors.New("must run service.config frist"))) } if server != nil { - panic(vm.NewGoError(errors.New("server already started"))) + // panic(vm.NewGoError(errors.New("server already started"))) + vm.SetData("_lastError", errors.New("server already started")) + gojs.GetLogger(vm).Error("server already started") + return nil } // 处理静态文件 if len(serviceConfig.Static) > 0 { @@ -362,7 +371,10 @@ func init() { }, "stop": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { if server == nil { - panic(vm.NewGoError(errors.New("server not started"))) + // panic(vm.NewGoError(errors.New("server not started"))) + vm.SetData("_lastError", errors.New("server not started")) + gojs.GetLogger(vm).Error("server not started") + return nil } server.Stop() s.ResetAllSets() @@ -480,7 +492,10 @@ func init() { o := args.Obj(0) action := args.Func(1) if action == nil { - panic(vm.NewGoError(errors.New("action must be a callback function"))) + // panic(vm.NewGoError(errors.New("action must be a callback function"))) + vm.SetData("_lastError", errors.New("action must be a callback function")) + gojs.GetLogger(vm).Error("action must be a callback function") + return nil } authLevel := o.Int("authLevel") @@ -597,11 +612,17 @@ func init() { fi := u.GetFileInfo(actionFile) if fi == nil { fullActionFile, _ := filepath.Abs(actionFile) - panic(vm.NewGoError(errors.New("actionFile must be a js file path: " + fullActionFile))) + // panic(vm.NewGoError(errors.New("actionFile must be a js file path: " + fullActionFile))) + vm.SetData("_lastError", errors.New("actionFile must be a js file path: "+fullActionFile)) + gojs.GetLogger(vm).Error("actionFile must be a js file path: " + fullActionFile) + return nil } actionCode := u.ReadFileN(actionFile) if !strings.Contains(actionCode, "function main(") { // || !strings.Contains(actionCode, ".register(") - panic(vm.NewGoError(errors.New("actionFile must be a js file with main function"))) + // panic(vm.NewGoError(errors.New("actionFile must be a js file with main function"))) + vm.SetData("_lastError", errors.New("actionFile must be a js file with main function")) + gojs.GetLogger(vm).Error("actionFile must be a js file with main function") + return nil } poolsLock.Lock() poolExists[actionFile] = true @@ -654,49 +675,80 @@ func init() { // }) // return nil // }, - "setTplFunc": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { + // "setTplFunc": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { + // args := gojs.MakeArgs(&argsIn, vm).Check(1) + // fnObj := args.Obj(0) + // if fnObj != nil { + // fnList := map[string]any{} + // for _, k := range fnObj.O.Keys() { + // if jsFunc := fnObj.Func(k); jsFunc != nil { + // fn := func(args ...any) any { + // jsArgs := make([]goja.Value, len(args)) + // for i := 0; i < len(args); i++ { + // jsArgs[i] = vm.ToValue(args[i]) + // } + // // vm.CallbackLocker.Lock() + // r, err := jsFunc(argsIn.This, jsArgs...) + // // vm.CallbackLocker.Unlock() + // if err == nil { + // return r.Export() + // } else { + // panic(vm.NewGoError(err)) + // } + // } + // fnList[k] = fn + // } + // } + // if len(fnList) > 0 { + // tplLock.Lock() + // for k, v := range fnList { + // tplFunc[k] = v + // } + // tplLock.Unlock() + // } + // } + // return nil + // }, + "setTplReplaces": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(1) - fnObj := args.Obj(0) - if fnObj != nil { - fnList := map[string]any{} - for _, k := range fnObj.O.Keys() { - if jsFunc := fnObj.Func(k); jsFunc != nil { - fn := func(args ...any) any { - jsArgs := make([]goja.Value, len(args)) - for i := 0; i < len(args); i++ { - jsArgs[i] = vm.ToValue(args[i]) - } - if r, err := jsFunc(argsIn.This, jsArgs...); err == nil { - return r.Export() - } else { - panic(vm.NewGoError(err)) - } - } - fnList[k] = fn - } - } - if len(fnList) > 0 { - tplFuncLock.Lock() - for k, v := range fnList { - tplFunc[k] = v - } - tplFuncLock.Unlock() - } + replaces := args.Map(0) + tplLock.Lock() + for k, v := range replaces { + tplReplaces[k] = u.String(v) } + tplLock.Unlock() return nil }, "tpl": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(2) filename := args.Path(0) + if !tplIsSafe(filename) { + // panic(vm.NewGoError(errors.New("tpl file " + filename + " not in safe paths"))) + vm.SetData("_lastError", errors.New("tpl file "+filename+" not in safe paths")) + gojs.GetLogger(vm).Error("tpl file " + filename + " not in safe paths") + return nil + } + + basepath := filepath.Dir(filename) info := u.GetFileInfo(filename) if info == nil { - panic(vm.NewGoError(errors.New("tpl file " + filename + " not exists"))) + // panic(vm.NewGoError(errors.New("tpl file " + filename + " not exists"))) + vm.SetData("_lastError", errors.New("tpl file "+filename+" not exists")) + gojs.GetLogger(vm).Error("tpl file " + filename + " not exists") + return nil } data := args.Any(1) - tplCacheLock.RLock() - t := tplCache[filename] - tplCacheLock.RUnlock() + // tplLock.RLock() + // t := tplCache[filename] + var t *TplCache + if t1 := vm.GetData("TPL_" + filename); t1 != nil { + if t2, ok := t1.(*TplCache); ok { + t = t2 + } + } + // t := vm.GetData("TPL_" + filename).(*TplCache) + // tplLock.RUnlock() if t != nil { for f, tm := range t.FileModTime { info := u.GetFileInfo(f) @@ -707,44 +759,110 @@ func init() { } } if t == nil { - tpl := template.New("main") - if len(tplFunc) > 0 { - tpl = tpl.Funcs(tplFunc) + tpl := template.New(filename) + fnList := map[string]any{} + if fnObj := args.Obj(2); fnObj != nil { + for _, k := range fnObj.O.Keys() { + if jsFunc := fnObj.Func(k); jsFunc != nil { + fn := func(args ...any) any { + jsArgs := make([]goja.Value, len(args)) + for i := 0; i < len(args); i++ { + jsArgs[i] = vm.ToValue(args[i]) + } + r, err := jsFunc(argsIn.This, jsArgs...) + if err == nil { + return r.Export() + } else { + // panic(vm.NewGoError(err)) + vm.SetData("_lastError", err) + gojs.GetLogger(vm).Error(err.Error()) + return nil + } + } + fnList[k] = fn + } + } + } + if len(fnList) > 0 { + tpl = tpl.Funcs(fnList) } fileModTime := map[string]int64{ filename: info.ModTime.UnixMilli(), } var err error - for _, m := range tplIncludeMatcher.FindAllStringSubmatch(u.ReadFileN(filename), -1) { - includeFilename := m[1] - info2 := u.GetFileInfo(includeFilename) - if info2 == nil { - includeFilename = filepath.Join(filepath.Dir(filename), m[1]) - info2 = u.GetFileInfo(includeFilename) + code := tplReplace(u.ReadFileN(filename)) + for _, m := range tplIncludeMatcher.FindAllStringSubmatch(code, -1) { + a := strings.SplitN(m[1], ":", 2) + includeFilename := a[0] + includeFilepath := filepath.Join(basepath, includeFilename) + if !tplIsSafe(includeFilepath) { + // panic(vm.NewGoError(errors.New("tpl file " + includeFilepath + " not in safe paths"))) + vm.SetData("_lastError", errors.New("tpl file "+includeFilepath+" not in safe paths")) + gojs.GetLogger(vm).Error("tpl file " + includeFilepath + " not in safe paths") + return nil } - if info2 != nil { - tpl, err = tpl.Parse(`{{ define "` + m[1] + `" }}` + u.ReadFileN(includeFilename) + `{{ end }}`) - if err != nil { - panic(vm.NewGoError(err)) + + // 每个被包含的文件只解析一次(统一修正) + if fileModTime[includeFilepath] == 0 { + info2 := u.GetFileInfo(includeFilepath) + if info2 != nil { + includeCode := tplReplace(u.ReadFileN(includeFilepath)) + foundBlock := false + includeCode = tplDefineMatcher.ReplaceAllStringFunc(includeCode, func(str string) string { + if m := tplDefineMatcher.FindStringSubmatch(str); len(m) > 1 { + foundBlock = true + if m[1] == "main" { + return `{{ define "` + includeFilename + `" }}` + } + return `{{ define "` + includeFilename + ":" + m[1] + `" }}` + } + return str + }) + if !foundBlock { + includeCode = `{{ define "` + includeFilename + `" }}` + includeCode + `{{ end }}` + } + tpl, err = tpl.Parse(includeCode) + if err != nil { + // panic(vm.NewGoError(err)) + vm.SetData("_lastError", err) + gojs.GetLogger(vm).Error(err.Error()) + return nil + } + fileModTime[includeFilepath] = info2.ModTime.UnixMilli() + } else { + // panic(vm.NewGoError(errors.New("tpl file " + includeFilepath + " not exists"))) + vm.SetData("_lastError", errors.New("tpl file "+includeFilepath+" not exists")) + gojs.GetLogger(vm).Error("tpl file " + includeFilepath + " not exists") + return nil } - fileModTime[includeFilename] = info2.ModTime.UnixMilli() } } - tpl, err = tpl.ParseFiles(filename) + + tpl, err = tpl.Parse(code) if err != nil { - panic(vm.NewGoError(err)) + // panic(vm.NewGoError(err)) + vm.SetData("_lastError", err) + gojs.GetLogger(vm).Error(err.Error()) + return nil } t = &TplCache{ Tpl: tpl, FileModTime: fileModTime, } + // tplLock.Lock() + // tplCache[filename] = t + // tplLock.Unlock() + vm.SetData("TPL_"+filename, t) } buf := bytes.NewBuffer(make([]byte, 0)) - err := t.Tpl.ExecuteTemplate(buf, filepath.Base(filename), data) + err := t.Tpl.ExecuteTemplate(buf, filename, data) if err != nil { - panic(vm.NewGoError(err)) + // panic(vm.NewGoError(err)) + vm.SetData("_lastError", err) + gojs.GetLogger(vm).Error(err.Error()) + return nil } return vm.ToValue(buf.String()) }, @@ -784,10 +902,53 @@ func init() { }) } +var regexpCache = map[string]*regexp.Regexp{} +var regexpLock = sync.RWMutex{} + +func getRegexp(regexpStr string) (*regexp.Regexp, error) { + regexpLock.RLock() + rx, ok := regexpCache[regexpStr] + regexpLock.RUnlock() + if ok { + return rx, nil + } + rx, err := regexp.Compile(regexpStr) + if err == nil { + regexpLock.Lock() + defer regexpLock.Unlock() + regexpCache[regexpStr] = rx + } + return rx, err +} + +func tplReplace(code string) string { + for k, v := range tplReplaces { + if rx, err := getRegexp(k); err == nil { + code = rx.ReplaceAllString(code, v) + } + } + return code +} + +func tplIsSafe(filename string) bool { + if len(serviceConfig.TplSafePaths) > 0 { + isSafe := false + for _, safePath := range serviceConfig.TplSafePaths { + if strings.HasPrefix(filename, safePath) { + isSafe = true + break + } + } + return isSafe + } + return true +} + var tplIncludeMatcher = regexp.MustCompile(`{{\s*template\s+"([^"]+)"`) +var tplDefineMatcher = regexp.MustCompile(`{{\s*define\s+"([^"]+)"\s*}}`) func verifyRegexp(regexpStr string) func(any, *goja.Runtime) bool { - if rx, err := regexp.Compile(regexpStr); err != nil { + if rx, err := getRegexp(regexpStr); err != nil { return func(value any, vm *goja.Runtime) bool { return rx.MatchString(u.String(value)) } @@ -907,6 +1068,15 @@ func runAction(action goja.Callable, vm *goja.Runtime, thisArg goja.Value, args } } + // force set userId to vm + vmUserId := "_guest" + if session != nil { + if userId, ok := session.data[serviceConfig.UserIdKey]; ok { + vmUserId = u.String(userId) + } + } + vm.SetData("userId", vmUserId) + requestParams, resp := makeRequestParams(args, headers, request, response, client, caller, session, logger) var r any diff --git a/service.ts b/service.ts index b695e86..888ea61 100644 --- a/service.ts +++ b/service.ts @@ -22,7 +22,8 @@ export default { // idL, // uniqueId, // uniqueIdL, - setTplFunc, + // setTplFunc, + setTplReplaces, tpl, } @@ -52,8 +53,9 @@ function id(size?: number): string { return '' } // function idL(space: string, size?: number): string { return '' } // function uniqueId(size?: number): string { return '' } // function uniqueIdL(size?: number): string { return '' } -function setTplFunc(fnList: Object): void { } -function tpl(file: string, data: Object): string { return '' } +// function setTplFunc(fnList: Object): void { } +function setTplReplaces(replaces: Object): void { } +function tpl(file: string, data: Object, fnList?: Object): string { return '' } export interface Config { // github.com/ssgo/s 的配置参数 @@ -118,6 +120,7 @@ export interface Config { limiterRedis: string // 限流器使用的Redis连接,默认使用内存存储 limiters: Map // 限流器配置,from 为数据来源,例如:ip、user、device、header.User-Agent、in.phone 等(in表示从请求参数中获取),time为时间间隔,单位ms,times为 时间间隔内允许访问的次数 hotLoad: number // 热加载配置,单位s,默认值:0,0表示不检测热加载 + tplSafePaths: string[] // 模板文件的安全路径,默认不开启安全检查 // gateway 的配置参数 proxy: Map // 代理配置,key为[host][path],value为代理的目标应用或URL diff --git a/tests/service_ws_test.go b/tests/service_ws_test.go index 9b59209..9a30619 100644 --- a/tests/service_ws_test.go +++ b/tests/service_ws_test.go @@ -45,7 +45,9 @@ func TestWS(t *testing.T) { } func TestStopWSByPool(t *testing.T) { + fmt.Println(u.BGreen("stop ws")) _, err := rt3.RunCode("s.stop();task.stop();") + fmt.Println(u.BGreen("stop ws ok"), err) if err != nil { t.Fatal("stop failed", err) } diff --git a/tests/task.js b/tests/task.js index 51f5f3e..d9e1ca6 100644 --- a/tests/task.js +++ b/tests/task.js @@ -3,32 +3,29 @@ import co from 'apigo.cc/gojs/console' import task from 'apigo.cc/gojs/task' function onStart() { - co.info('task start') + co.info('task start') } let i = 0 function onRun() { - let keys = task.keys('wsTest_') - // let connCount = s.dataCount('wsTest') - if (keys.length > 0) { - // let conns = s.dataFetch('wsTest') - let conns = task.getAll('wsTest_') - for (let id in conns) { - let conn = conns[id] - try { - conn.write(i++) - } catch (e) { - co.error(e) - task.remove(id) - } - } - } - co.info('task run', keys.length) + let keys = task.keys('wsTest_') + // let connCount = s.dataCount('wsTest') + if (keys.length > 0) { + // let conns = s.dataFetch('wsTest') + let conns = task.getAll('wsTest_') + for (let id in conns) { + let conn = conns[id] + if (!conn.write(i++)) { + task.remove(id) + } + } + } + co.info('task run', keys.length) } function onStop() { - // s.dataRemove('wsTest') - task.removeAll('wsTest_') - co.info('task stop') + // s.dataRemove('wsTest') + task.removeAll('wsTest_') + co.info('task stop') } diff --git a/tests/tpl.js b/tests/tpl.js index 2b47860..d409424 100644 --- a/tests/tpl.js +++ b/tests/tpl.js @@ -1,8 +1,7 @@ import s from "apigo.cc/gojs/service" function main(args) { - s.setTplFunc({ - bb: text => { return '' + text + '' } - }) - return s.tpl('tpl/page.html', { title: 'Abc' }) + return s.tpl('tpl/page.html', { title: 'Abc' }, { + bb: text => { return '' + text + '' } + }) } \ No newline at end of file diff --git a/tests/tpl/header.html b/tests/tpl/header.html index a175b49..fc62fa3 100644 --- a/tests/tpl/header.html +++ b/tests/tpl/header.html @@ -1,3 +1,3 @@
-

Welcome to {{bb .title}}

+

Welcome to {{bb .title}}

\ No newline at end of file diff --git a/tests/tpl/page.html b/tests/tpl/page.html index 939ba35..f03aad4 100644 --- a/tests/tpl/page.html +++ b/tests/tpl/page.html @@ -2,15 +2,15 @@ - - - - {{.title}} + + + + {{.title}} - {{template "header.html" .}} -
+	{{template "header.html" .}}
+	
       hello world
     
diff --git a/ws.go b/ws.go index 181283b..c14d1a3 100644 --- a/ws.go +++ b/ws.go @@ -18,7 +18,10 @@ func MakeWSClient(client *websocket.Conn, id string) gojs.Map { "read": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { typ, data, err := readWSMessage(client) if err != nil { - panic(vm.NewGoError(err)) + // panic(vm.NewGoError(err)) + vm.SetData("_lastError", err) + gojs.GetLogger(vm).Error(err.Error()) + return vm.ToValue(false) } return vm.ToValue(gojs.Map{ "type": typ, @@ -37,16 +40,22 @@ func MakeWSClient(client *websocket.Conn, id string) gojs.Map { err = client.WriteMessage(websocket.TextMessage, u.JsonBytes(args.Any(0))) } if err != nil { - panic(vm.NewGoError(err)) + // panic(vm.NewGoError(err)) + vm.SetData("_lastError", err) + gojs.GetLogger(vm).Error(err.Error()) + return vm.ToValue(false) } - return nil + return vm.ToValue(true) }, "ping": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { err := client.WriteMessage(websocket.PingMessage, nil) if err != nil { - panic(vm.NewGoError(err)) + // panic(vm.NewGoError(err)) + vm.SetData("_lastError", err) + gojs.GetLogger(vm).Error(err.Error()) + return vm.ToValue(false) } - return nil + return vm.ToValue(true) }, "writeMessage": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { args := gojs.MakeArgs(&argsIn, vm).Check(2) @@ -68,16 +77,22 @@ func MakeWSClient(client *websocket.Conn, id string) gojs.Map { err = client.WriteMessage(websocket.TextMessage, args.Bytes(1)) } if err != nil { - panic(vm.NewGoError(err)) + // panic(vm.NewGoError(err)) + vm.SetData("_lastError", err) + gojs.GetLogger(vm).Error(err.Error()) + return vm.ToValue(false) } - return nil + return vm.ToValue(true) }, "close": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { err := client.Close() if err != nil { - panic(vm.NewGoError(err)) + // panic(vm.NewGoError(err)) + vm.SetData("_lastError", err) + gojs.GetLogger(vm).Error(err.Error()) + return vm.ToValue(false) } - return nil + return vm.ToValue(true) }, } }