升级异常处理机制

This commit is contained in:
Star 2025-12-12 21:20:57 +08:00
parent fa97cace29
commit 627c05fd06
11 changed files with 308 additions and 110 deletions

View File

@ -22,7 +22,10 @@ func NewCaller(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
func makeResult(r *httpclient.Result, vm *goja.Runtime) goja.Value { func makeResult(r *httpclient.Result, vm *goja.Runtime) goja.Value {
if r.Error != nil { 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{} headers := map[string]string{}
for k, v := range r.Response.Header { for k, v := range r.Response.Header {

4
go.mod
View File

@ -3,7 +3,7 @@ module apigo.cc/gojs/service
go 1.24.0 go 1.24.0
require ( require (
apigo.cc/gojs v0.0.31 apigo.cc/gojs v0.0.32
apigo.cc/gojs/console v0.0.4 apigo.cc/gojs/console v0.0.4
apigo.cc/gojs/file v0.0.7 apigo.cc/gojs/file v0.0.7
apigo.cc/gojs/http v0.0.8 apigo.cc/gojs/http v0.0.8
@ -14,7 +14,7 @@ require (
github.com/ssgo/config v1.7.10 github.com/ssgo/config v1.7.10
github.com/ssgo/discover v1.7.10 github.com/ssgo/discover v1.7.10
github.com/ssgo/httpclient v1.7.8 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/redis v1.7.8
github.com/ssgo/s v1.7.25 github.com/ssgo/s v1.7.25
github.com/ssgo/standard v1.7.7 github.com/ssgo/standard v1.7.7

View File

@ -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 { func (r *Request) ReadAll(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
data, err := io.ReadAll(r.req.Body) data, err := io.ReadAll(r.req.Body)
if err != nil { 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) return vm.ToValue(data)
} }
@ -102,7 +105,10 @@ func (r *Request) Read(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
data := make([]byte, size) data := make([]byte, size)
n, err := r.req.Body.Read(data) n, err := r.req.Body.Read(data)
if err != nil { 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]) 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 { func (r *Request) Close(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
err := r.req.Body.Close() err := r.req.Body.Close()
if err != nil { 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 return nil
} }

View File

@ -50,10 +50,11 @@ type TplCache struct {
Tpl *template.Template Tpl *template.Template
} }
var tplFunc = map[string]any{} // var tplFunc = map[string]any{}
var tplFuncLock = sync.RWMutex{} var tplReplaces = map[string]string{}
var tplCache = map[string]*TplCache{} var tplLock = sync.RWMutex{}
var tplCacheLock = sync.RWMutex{}
// var tplCache = map[string]*TplCache{}
type LimiterConfig struct { type LimiterConfig struct {
From string From string
@ -74,6 +75,7 @@ type Config struct {
Limiters map[string]*LimiterConfig Limiters map[string]*LimiterConfig
LimiterRedis string LimiterRedis string
HotLoad int HotLoad int
TplSafePaths []string
Proxy map[string]string Proxy map[string]string
Rewrite 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)) 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 { 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) config.LoadConfig("service", &discover.Config)
// var auth goja.Callable // var auth goja.Callable
@ -138,6 +143,7 @@ func initConfig(opt *gojs.Obj, logger *log.Logger, vm *goja.Runtime) {
} }
} }
} }
// 如果没有session中的authLevel验证失败则使用Access-Token中的authLevel服务间调用 // 如果没有session中的authLevel验证失败则使用Access-Token中的authLevel服务间调用
if authAccessToken && setAuthLevel < authLevel { if authAccessToken && setAuthLevel < authLevel {
setAuthLevel = s.GetAuthTokenLevel(request.Header.Get("Access-Token")) setAuthLevel = s.GetAuthTokenLevel(request.Header.Get("Access-Token"))
@ -251,7 +257,10 @@ func init() {
// panic(vm.NewGoError(errors.New("must run service.config frist"))) // panic(vm.NewGoError(errors.New("must run service.config frist")))
} }
if server != nil { 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 { if len(serviceConfig.Static) > 0 {
@ -362,7 +371,10 @@ func init() {
}, },
"stop": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { "stop": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
if server == nil { 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() server.Stop()
s.ResetAllSets() s.ResetAllSets()
@ -480,7 +492,10 @@ func init() {
o := args.Obj(0) o := args.Obj(0)
action := args.Func(1) action := args.Func(1)
if action == nil { 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") authLevel := o.Int("authLevel")
@ -597,11 +612,17 @@ func init() {
fi := u.GetFileInfo(actionFile) fi := u.GetFileInfo(actionFile)
if fi == nil { if fi == nil {
fullActionFile, _ := filepath.Abs(actionFile) 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) actionCode := u.ReadFileN(actionFile)
if !strings.Contains(actionCode, "function main(") { // || !strings.Contains(actionCode, ".register(") 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() poolsLock.Lock()
poolExists[actionFile] = true poolExists[actionFile] = true
@ -654,49 +675,80 @@ func init() {
// }) // })
// return nil // 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) args := gojs.MakeArgs(&argsIn, vm).Check(1)
fnObj := args.Obj(0) replaces := args.Map(0)
if fnObj != nil { tplLock.Lock()
fnList := map[string]any{} for k, v := range replaces {
for _, k := range fnObj.O.Keys() { tplReplaces[k] = u.String(v)
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()
}
} }
tplLock.Unlock()
return nil return nil
}, },
"tpl": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { "tpl": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
args := gojs.MakeArgs(&argsIn, vm).Check(2) args := gojs.MakeArgs(&argsIn, vm).Check(2)
filename := args.Path(0) 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) info := u.GetFileInfo(filename)
if info == nil { 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) data := args.Any(1)
tplCacheLock.RLock() // tplLock.RLock()
t := tplCache[filename] // t := tplCache[filename]
tplCacheLock.RUnlock() 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 { if t != nil {
for f, tm := range t.FileModTime { for f, tm := range t.FileModTime {
info := u.GetFileInfo(f) info := u.GetFileInfo(f)
@ -707,44 +759,110 @@ func init() {
} }
} }
if t == nil { if t == nil {
tpl := template.New("main") tpl := template.New(filename)
if len(tplFunc) > 0 { fnList := map[string]any{}
tpl = tpl.Funcs(tplFunc) 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{ fileModTime := map[string]int64{
filename: info.ModTime.UnixMilli(), filename: info.ModTime.UnixMilli(),
} }
var err error var err error
for _, m := range tplIncludeMatcher.FindAllStringSubmatch(u.ReadFileN(filename), -1) { code := tplReplace(u.ReadFileN(filename))
includeFilename := m[1] for _, m := range tplIncludeMatcher.FindAllStringSubmatch(code, -1) {
info2 := u.GetFileInfo(includeFilename) a := strings.SplitN(m[1], ":", 2)
if info2 == nil { includeFilename := a[0]
includeFilename = filepath.Join(filepath.Dir(filename), m[1]) includeFilepath := filepath.Join(basepath, includeFilename)
info2 = u.GetFileInfo(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 { if fileModTime[includeFilepath] == 0 {
panic(vm.NewGoError(err)) 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 { 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{ t = &TplCache{
Tpl: tpl, Tpl: tpl,
FileModTime: fileModTime, FileModTime: fileModTime,
} }
// tplLock.Lock()
// tplCache[filename] = t
// tplLock.Unlock()
vm.SetData("TPL_"+filename, t)
} }
buf := bytes.NewBuffer(make([]byte, 0)) 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 { 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()) 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 tplIncludeMatcher = regexp.MustCompile(`{{\s*template\s+"([^"]+)"`)
var tplDefineMatcher = regexp.MustCompile(`{{\s*define\s+"([^"]+)"\s*}}`)
func verifyRegexp(regexpStr string) func(any, *goja.Runtime) bool { 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 func(value any, vm *goja.Runtime) bool {
return rx.MatchString(u.String(value)) 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) requestParams, resp := makeRequestParams(args, headers, request, response, client, caller, session, logger)
var r any var r any

View File

@ -22,7 +22,8 @@ export default {
// idL, // idL,
// uniqueId, // uniqueId,
// uniqueIdL, // uniqueIdL,
setTplFunc, // setTplFunc,
setTplReplaces,
tpl, tpl,
} }
@ -52,8 +53,9 @@ function id(size?: number): string { return '' }
// function idL(space: string, size?: number): string { return '' } // function idL(space: string, size?: number): string { return '' }
// function uniqueId(size?: number): string { return '' } // function uniqueId(size?: number): string { return '' }
// function uniqueIdL(size?: number): string { return '' } // function uniqueIdL(size?: number): string { return '' }
function setTplFunc(fnList: Object): void { } // function setTplFunc(fnList: Object): void { }
function tpl(file: string, data: Object): string { return '' } function setTplReplaces(replaces: Object): void { }
function tpl(file: string, data: Object, fnList?: Object): string { return '' }
export interface Config { export interface Config {
// github.com/ssgo/s 的配置参数 // github.com/ssgo/s 的配置参数
@ -118,6 +120,7 @@ export interface Config {
limiterRedis: string // 限流器使用的Redis连接默认使用内存存储 limiterRedis: string // 限流器使用的Redis连接默认使用内存存储
limiters: Map<string, LimiterConfig> // 限流器配置from 为数据来源例如ip、user、device、header.User-Agent、in.phone 等in表示从请求参数中获取time为时间间隔单位mstimes为 时间间隔内允许访问的次数 limiters: Map<string, LimiterConfig> // 限流器配置from 为数据来源例如ip、user、device、header.User-Agent、in.phone 等in表示从请求参数中获取time为时间间隔单位mstimes为 时间间隔内允许访问的次数
hotLoad: number // 热加载配置单位s默认值00表示不检测热加载 hotLoad: number // 热加载配置单位s默认值00表示不检测热加载
tplSafePaths: string[] // 模板文件的安全路径,默认不开启安全检查
// gateway 的配置参数 // gateway 的配置参数
proxy: Map<string, string> // 代理配置key为[host][path]value为代理的目标应用或URL proxy: Map<string, string> // 代理配置key为[host][path]value为代理的目标应用或URL

View File

@ -45,7 +45,9 @@ func TestWS(t *testing.T) {
} }
func TestStopWSByPool(t *testing.T) { func TestStopWSByPool(t *testing.T) {
fmt.Println(u.BGreen("stop ws"))
_, err := rt3.RunCode("s.stop();task.stop();") _, err := rt3.RunCode("s.stop();task.stop();")
fmt.Println(u.BGreen("stop ws ok"), err)
if err != nil { if err != nil {
t.Fatal("stop failed", err) t.Fatal("stop failed", err)
} }

View File

@ -3,32 +3,29 @@ import co from 'apigo.cc/gojs/console'
import task from 'apigo.cc/gojs/task' import task from 'apigo.cc/gojs/task'
function onStart() { function onStart() {
co.info('task start') co.info('task start')
} }
let i = 0 let i = 0
function onRun() { function onRun() {
let keys = task.keys('wsTest_') let keys = task.keys('wsTest_')
// let connCount = s.dataCount('wsTest') // let connCount = s.dataCount('wsTest')
if (keys.length > 0) { if (keys.length > 0) {
// let conns = s.dataFetch('wsTest') // let conns = s.dataFetch('wsTest')
let conns = task.getAll('wsTest_') let conns = task.getAll('wsTest_')
for (let id in conns) { for (let id in conns) {
let conn = conns[id] let conn = conns[id]
try { if (!conn.write(i++)) {
conn.write(i++) task.remove(id)
} catch (e) { }
co.error(e) }
task.remove(id) }
} co.info('task run', keys.length)
}
}
co.info('task run', keys.length)
} }
function onStop() { function onStop() {
// s.dataRemove('wsTest') // s.dataRemove('wsTest')
task.removeAll('wsTest_') task.removeAll('wsTest_')
co.info('task stop') co.info('task stop')
} }

View File

@ -1,8 +1,7 @@
import s from "apigo.cc/gojs/service" import s from "apigo.cc/gojs/service"
function main(args) { function main(args) {
s.setTplFunc({ return s.tpl('tpl/page.html', { title: 'Abc' }, {
bb: text => { return '<b>' + text + '</b>' } bb: text => { return '<b>' + text + '</b>' }
}) })
return s.tpl('tpl/page.html', { title: 'Abc' })
} }

View File

@ -1,3 +1,3 @@
<header> <header>
<h1>Welcome to {{bb .title}}</h1> <h1>Welcome to {{bb .title}}</h1>
</header> </header>

View File

@ -2,15 +2,15 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge"> <meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>{{.title}}</title> <title>{{.title}}</title>
</head> </head>
<body> <body>
{{template "header.html" .}} {{template "header.html" .}}
<pre> <pre>
hello world hello world
</pre> </pre>
</body> </body>

33
ws.go
View File

@ -18,7 +18,10 @@ func MakeWSClient(client *websocket.Conn, id string) gojs.Map {
"read": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { "read": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
typ, data, err := readWSMessage(client) typ, data, err := readWSMessage(client)
if err != 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 vm.ToValue(gojs.Map{ return vm.ToValue(gojs.Map{
"type": typ, "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))) err = client.WriteMessage(websocket.TextMessage, u.JsonBytes(args.Any(0)))
} }
if err != 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)
}, },
"ping": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { "ping": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
err := client.WriteMessage(websocket.PingMessage, nil) err := client.WriteMessage(websocket.PingMessage, nil)
if err != 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 { "writeMessage": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
args := gojs.MakeArgs(&argsIn, vm).Check(2) 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)) err = client.WriteMessage(websocket.TextMessage, args.Bytes(1))
} }
if err != 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)
}, },
"close": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { "close": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
err := client.Close() err := client.Close()
if err != 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)
}, },
} }
} }