Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ee7007f050 | |||
| 627c05fd06 | |||
| fa97cace29 | |||
| 069a4b95c2 | |||
| e64727d2ef | |||
| baa4d430cc | |||
| 57db518ef9 | |||
| 715de5e442 |
@ -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 {
|
||||||
|
|||||||
56
go.mod
56
go.mod
@ -1,44 +1,54 @@
|
|||||||
module apigo.cc/gojs/service
|
module apigo.cc/gojs/service
|
||||||
|
|
||||||
go 1.18
|
go 1.24.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
apigo.cc/gojs v0.0.12
|
apigo.cc/gojs v0.0.32
|
||||||
apigo.cc/gojs/console v0.0.2
|
apigo.cc/gojs/console v0.0.4
|
||||||
apigo.cc/gojs/http v0.0.3
|
apigo.cc/gojs/file v0.0.7
|
||||||
apigo.cc/gojs/util v0.0.8
|
apigo.cc/gojs/http v0.0.8
|
||||||
|
apigo.cc/gojs/runtime v0.0.4
|
||||||
|
apigo.cc/gojs/task v0.0.8
|
||||||
|
apigo.cc/gojs/util v0.0.16
|
||||||
github.com/gorilla/websocket v1.5.3
|
github.com/gorilla/websocket v1.5.3
|
||||||
github.com/ssgo/config v1.7.9
|
github.com/ssgo/config v1.7.10
|
||||||
github.com/ssgo/discover v1.7.9
|
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.7
|
github.com/ssgo/log v1.7.10
|
||||||
github.com/ssgo/redis v1.7.7
|
github.com/ssgo/redis v1.7.8
|
||||||
github.com/ssgo/s v1.7.22
|
github.com/ssgo/s v1.7.25
|
||||||
github.com/ssgo/standard v1.7.7
|
github.com/ssgo/standard v1.7.7
|
||||||
github.com/ssgo/u v1.7.13
|
github.com/ssgo/u v1.7.23
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/ZZMarquis/gm v1.3.2 // indirect
|
github.com/ZZMarquis/gm v1.3.2 // indirect
|
||||||
github.com/dlclark/regexp2 v1.11.4 // indirect
|
github.com/dlclark/regexp2 v1.11.5 // indirect
|
||||||
github.com/emmansun/gmsm v0.29.6 // indirect
|
github.com/emmansun/gmsm v0.40.0 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.8.0 // indirect
|
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||||
|
github.com/go-rod/rod v0.116.2 // indirect
|
||||||
github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect
|
github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect
|
||||||
github.com/gomodule/redigo v1.9.2 // indirect
|
github.com/gomodule/redigo v1.9.2 // indirect
|
||||||
github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect
|
github.com/google/pprof v0.0.0-20250903194437-c28834ac2320 // indirect
|
||||||
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 // indirect
|
github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 // indirect
|
||||||
github.com/obscuren/ecies v0.0.0-20150213224233-7c0f4a9b18d9 // indirect
|
github.com/obscuren/ecies v0.0.0-20150213224233-7c0f4a9b18d9 // indirect
|
||||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||||
|
github.com/robfig/cron/v3 v3.0.1 // indirect
|
||||||
github.com/shirou/gopsutil/v3 v3.24.5 // indirect
|
github.com/shirou/gopsutil/v3 v3.24.5 // indirect
|
||||||
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||||
github.com/ssgo/tool v0.4.28 // indirect
|
github.com/ssgo/tool v0.4.29 // indirect
|
||||||
github.com/tklauser/go-sysconf v0.3.14 // indirect
|
github.com/tklauser/go-sysconf v0.3.15 // indirect
|
||||||
github.com/tklauser/numcpus v0.9.0 // indirect
|
github.com/tklauser/numcpus v0.10.0 // indirect
|
||||||
|
github.com/ysmood/fetchup v0.2.3 // indirect
|
||||||
|
github.com/ysmood/goob v0.4.0 // indirect
|
||||||
|
github.com/ysmood/got v0.40.0 // indirect
|
||||||
|
github.com/ysmood/gson v0.7.3 // indirect
|
||||||
|
github.com/ysmood/leakless v0.9.0 // indirect
|
||||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||||
golang.org/x/crypto v0.31.0 // indirect
|
golang.org/x/crypto v0.46.0 // indirect
|
||||||
golang.org/x/net v0.32.0 // indirect
|
golang.org/x/net v0.48.0 // indirect
|
||||||
golang.org/x/sys v0.28.0 // indirect
|
golang.org/x/sys v0.39.0 // indirect
|
||||||
golang.org/x/text v0.21.0 // indirect
|
golang.org/x/text v0.32.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
15
request.go
15
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 {
|
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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -93,7 +93,10 @@ func (r *Response) Write(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value
|
|||||||
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||||
n, err := r.resp.Write(args.Bytes(0))
|
n, err := r.resp.Write(args.Bytes(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 vm.ToValue(n)
|
return vm.ToValue(n)
|
||||||
}
|
}
|
||||||
|
|||||||
662
service.go
662
service.go
@ -37,6 +37,8 @@ var serviceMD string
|
|||||||
var server *s.AsyncServer
|
var server *s.AsyncServer
|
||||||
|
|
||||||
var pools = map[string]*gojs.Pool{}
|
var pools = map[string]*gojs.Pool{}
|
||||||
|
var poolsMTime = map[string]int64{}
|
||||||
|
var poolsConfig = map[string]gojs.PoolConfig{}
|
||||||
|
|
||||||
var poolExists = map[string]bool{}
|
var poolExists = map[string]bool{}
|
||||||
var poolActionRegistered = map[string]bool{}
|
var poolActionRegistered = map[string]bool{}
|
||||||
@ -48,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
|
||||||
@ -71,6 +74,8 @@ type Config struct {
|
|||||||
LimitedMessage string
|
LimitedMessage string
|
||||||
Limiters map[string]*LimiterConfig
|
Limiters map[string]*LimiterConfig
|
||||||
LimiterRedis string
|
LimiterRedis string
|
||||||
|
HotLoad int
|
||||||
|
TplSafePaths []string
|
||||||
|
|
||||||
Proxy map[string]string
|
Proxy map[string]string
|
||||||
Rewrite map[string]string
|
Rewrite map[string]string
|
||||||
@ -86,13 +91,16 @@ var configed = false
|
|||||||
func initConfig(opt *gojs.Obj, logger *log.Logger, vm *goja.Runtime) {
|
func initConfig(opt *gojs.Obj, logger *log.Logger, vm *goja.Runtime) {
|
||||||
configed = true
|
configed = true
|
||||||
s.InitConfig()
|
s.InitConfig()
|
||||||
if startPath, ok := vm.GoData["startPath"]; ok {
|
if startPath := vm.GetData("startPath"); startPath != nil {
|
||||||
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, "", 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); errs != nil && 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
|
||||||
@ -135,24 +143,38 @@ 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"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
authOk := false
|
||||||
if setAuthLevel >= authLevel {
|
if setAuthLevel >= authLevel {
|
||||||
|
authOk = true // 默认通过
|
||||||
|
if options != nil && options.Ext != nil && options.Ext["funcs"] != nil {
|
||||||
|
if needFuncs, ok := options.Ext["funcs"].([]string); ok && len(needFuncs) > 0 {
|
||||||
|
// 指定了细化的权限验证要求
|
||||||
|
request.Set("funcs", needFuncs)
|
||||||
|
authOk = session.authFuncs(needFuncs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if authOk {
|
||||||
return true, session
|
return true, session
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := serviceConfig.AuthFieldMessage
|
||||||
|
if strings.Contains(msg, "{{") {
|
||||||
|
msg = strings.ReplaceAll(msg, "{{TARGET_AUTHLEVEL}}", u.String(authLevel))
|
||||||
|
msg = strings.ReplaceAll(msg, "{{USER_AUTHLEVEL}}", u.String(setAuthLevel))
|
||||||
|
}
|
||||||
|
var obj any
|
||||||
|
if json.Unmarshal([]byte(msg), &obj) == nil {
|
||||||
|
return false, obj
|
||||||
} else {
|
} else {
|
||||||
msg := serviceConfig.AuthFieldMessage
|
return false, msg
|
||||||
if strings.Contains(msg, "{{") {
|
|
||||||
msg = strings.ReplaceAll(msg, "{{TARGET_AUTHLEVEL}}", u.String(authLevel))
|
|
||||||
msg = strings.ReplaceAll(msg, "{{USER_AUTHLEVEL}}", u.String(setAuthLevel))
|
|
||||||
}
|
|
||||||
var obj any
|
|
||||||
if json.Unmarshal([]byte(msg), &obj) == nil {
|
|
||||||
return false, obj
|
|
||||||
} else {
|
|
||||||
return false, msg
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
s.Init()
|
s.Init()
|
||||||
@ -180,10 +202,49 @@ func initConfig(opt *gojs.Obj, logger *log.Logger, vm *goja.Runtime) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if serviceConfig.HotLoad > 0 {
|
||||||
|
s.NewTimerServer("hotload", time.Duration(serviceConfig.HotLoad)*time.Second, func(b *bool) {
|
||||||
|
tmpPoolsMTimes := map[string]int64{}
|
||||||
|
poolsLock.RLock()
|
||||||
|
for actionFile, mtime := range poolsMTime {
|
||||||
|
tmpPoolsMTimes[actionFile] = mtime
|
||||||
|
}
|
||||||
|
poolsLock.RUnlock()
|
||||||
|
|
||||||
|
for actionFile, mtime := range tmpPoolsMTimes {
|
||||||
|
fi := u.GetFileInfo(actionFile)
|
||||||
|
if fi == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
newMtime := fi.ModTime.Unix()
|
||||||
|
if newMtime > mtime {
|
||||||
|
// 重新加载文件
|
||||||
|
actionCode := u.ReadFileN(actionFile)
|
||||||
|
if !strings.Contains(actionCode, "function main(") { // || !strings.Contains(actionCode, ".register(")
|
||||||
|
logger.Error("hotload file %s failed, must be a js file with main function", actionFile)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
poolsLock.RLock()
|
||||||
|
oldOpt := poolsConfig[actionFile]
|
||||||
|
poolsLock.RUnlock()
|
||||||
|
|
||||||
|
p := gojs.NewPoolByCode(actionCode, actionFile, oldOpt, logger)
|
||||||
|
poolsLock.Lock()
|
||||||
|
pools[actionFile] = p
|
||||||
|
poolsMTime[actionFile] = newMtime
|
||||||
|
poolsLock.Unlock()
|
||||||
|
logger.Info("hotload file %s success", actionFile, "mtime", fi.ModTime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, nil, nil)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
s.Config.KeepKeyCase = true
|
s.Config.KeepKeyCase = true
|
||||||
|
s.DontStartLogAuto = true
|
||||||
obj := map[string]any{
|
obj := map[string]any{
|
||||||
"config": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
"config": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||||
args := gojs.MakeArgs(&argsIn, vm)
|
args := gojs.MakeArgs(&argsIn, vm)
|
||||||
@ -196,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 vm.ToValue(false)
|
||||||
}
|
}
|
||||||
// 处理静态文件
|
// 处理静态文件
|
||||||
if len(serviceConfig.Static) > 0 {
|
if len(serviceConfig.Static) > 0 {
|
||||||
@ -212,10 +276,10 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 处理Watch
|
// 处理Watch
|
||||||
if vm.GoData["inWatch"] == true {
|
if inWatch := vm.GetData("inWatch"); inWatch != nil && inWatch.(bool) {
|
||||||
onWatchConn := map[string]*websocket.Conn{}
|
onWatchConn := map[string]*websocket.Conn{}
|
||||||
onWatchLock := sync.Mutex{}
|
onWatchLock := sync.Mutex{}
|
||||||
vm.GoData["onWatch"] = func(filename string) {
|
vm.SetData("onWatch", func(filename string) {
|
||||||
onWatchLock.Lock()
|
onWatchLock.Lock()
|
||||||
defer onWatchLock.Unlock()
|
defer onWatchLock.Unlock()
|
||||||
for id, conn := range onWatchConn {
|
for id, conn := range onWatchConn {
|
||||||
@ -223,7 +287,7 @@ func init() {
|
|||||||
delete(onWatchConn, id)
|
delete(onWatchConn, id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
s.AddShutdownHook(func() {
|
s.AddShutdownHook(func() {
|
||||||
for _, conn := range onWatchConn {
|
for _, conn := range onWatchConn {
|
||||||
conn.Close()
|
conn.Close()
|
||||||
@ -235,8 +299,9 @@ func init() {
|
|||||||
onWatchLock.Unlock()
|
onWatchLock.Unlock()
|
||||||
}, "")
|
}, "")
|
||||||
s.SetOutFilter(func(in map[string]any, request *s.Request, response *s.Response, out any, logger *log.Logger) (newOut any, isOver bool) {
|
s.SetOutFilter(func(in map[string]any, request *s.Request, response *s.Response, out any, logger *log.Logger) (newOut any, isOver bool) {
|
||||||
if strings.HasPrefix(response.Header().Get("Content-Type"), "text/html") {
|
contentType := response.Header().Get("Content-Type")
|
||||||
outStr := u.String(out)
|
outStr := u.String(out)
|
||||||
|
if strings.HasPrefix(contentType, "text/html") || (contentType == "" && strings.Contains(outStr, "<html")) {
|
||||||
if strings.Contains(outStr, "let _watchWS = null") {
|
if strings.Contains(outStr, "let _watchWS = null") {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
@ -306,84 +371,131 @@ 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 vm.ToValue(false)
|
||||||
}
|
}
|
||||||
server.Stop()
|
server.Stop()
|
||||||
s.ResetAllSets()
|
s.ResetAllSets()
|
||||||
return nil
|
return vm.ToValue(true)
|
||||||
},
|
|
||||||
"uniqueId": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
|
||||||
args := gojs.MakeArgs(&argsIn, vm)
|
|
||||||
size := args.Int(0)
|
|
||||||
var id string
|
|
||||||
if size >= 20 {
|
|
||||||
id = s.UniqueId20()
|
|
||||||
} else if size >= 16 {
|
|
||||||
id = s.UniqueId16()
|
|
||||||
} else if size >= 14 {
|
|
||||||
id = s.UniqueId14()
|
|
||||||
} else if size >= 12 {
|
|
||||||
id = s.UniqueId14()[0:12]
|
|
||||||
} else {
|
|
||||||
id = s.UniqueId()
|
|
||||||
}
|
|
||||||
return vm.ToValue(id)
|
|
||||||
},
|
|
||||||
"uniqueIdL": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
|
||||||
args := gojs.MakeArgs(&argsIn, vm)
|
|
||||||
size := args.Int(0)
|
|
||||||
var id string
|
|
||||||
if size >= 20 {
|
|
||||||
id = s.UniqueId20()
|
|
||||||
} else if size >= 16 {
|
|
||||||
id = s.UniqueId16()
|
|
||||||
} else if size >= 14 {
|
|
||||||
id = s.UniqueId14()
|
|
||||||
} else if size >= 12 {
|
|
||||||
id = s.UniqueId14()[0:12]
|
|
||||||
} else {
|
|
||||||
id = s.UniqueId()
|
|
||||||
}
|
|
||||||
return vm.ToValue(strings.ToLower(id))
|
|
||||||
},
|
},
|
||||||
|
// "uniqueId": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||||
|
// args := gojs.MakeArgs(&argsIn, vm)
|
||||||
|
// size := args.Int(0)
|
||||||
|
// var id string
|
||||||
|
// if size >= 20 {
|
||||||
|
// id = s.UniqueId20()
|
||||||
|
// } else if size >= 16 {
|
||||||
|
// id = s.UniqueId16()
|
||||||
|
// } else if size >= 14 {
|
||||||
|
// id = s.UniqueId14()
|
||||||
|
// } else if size >= 12 {
|
||||||
|
// id = s.UniqueId14()[0:12]
|
||||||
|
// } else {
|
||||||
|
// id = s.UniqueId()
|
||||||
|
// }
|
||||||
|
// return vm.ToValue(id)
|
||||||
|
// },
|
||||||
|
// "uniqueIdL": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||||
|
// args := gojs.MakeArgs(&argsIn, vm)
|
||||||
|
// size := args.Int(0)
|
||||||
|
// var id string
|
||||||
|
// if size >= 20 {
|
||||||
|
// id = s.UniqueId20()
|
||||||
|
// } else if size >= 16 {
|
||||||
|
// id = s.UniqueId16()
|
||||||
|
// } else if size >= 14 {
|
||||||
|
// id = s.UniqueId14()
|
||||||
|
// } else if size >= 12 {
|
||||||
|
// id = s.UniqueId14()[0:12]
|
||||||
|
// } else {
|
||||||
|
// id = s.UniqueId()
|
||||||
|
// }
|
||||||
|
// return vm.ToValue(strings.ToLower(id))
|
||||||
|
// },
|
||||||
"id": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
"id": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||||
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||||
space := args.Str(0)
|
size := args.Int(0)
|
||||||
size := args.Int(1)
|
if size == 0 {
|
||||||
var id string
|
size = 12
|
||||||
if size >= 12 {
|
|
||||||
id = s.Id12(space)
|
|
||||||
} else if size >= 10 {
|
|
||||||
id = s.Id10(space)
|
|
||||||
} else if size >= 8 {
|
|
||||||
id = s.Id8(space)
|
|
||||||
} else {
|
|
||||||
id = s.Id6(space)
|
|
||||||
}
|
}
|
||||||
return vm.ToValue(id)
|
if size > 20 {
|
||||||
},
|
size = 20
|
||||||
"idL": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
|
||||||
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
|
||||||
space := args.Str(0)
|
|
||||||
size := args.Int(1)
|
|
||||||
var id string
|
|
||||||
if size >= 12 {
|
|
||||||
id = s.Id12(space)
|
|
||||||
} else if size >= 10 {
|
|
||||||
id = s.Id10(space)
|
|
||||||
} else if size >= 8 {
|
|
||||||
id = s.Id8(space)
|
|
||||||
} else {
|
|
||||||
id = s.Id6(space)
|
|
||||||
}
|
}
|
||||||
return vm.ToValue(strings.ToLower(id))
|
|
||||||
|
rd := sessionRedis
|
||||||
|
if rd == nil {
|
||||||
|
// 使用u.Id
|
||||||
|
return vm.ToValue(u.UniqueId()[0:size])
|
||||||
|
}
|
||||||
|
|
||||||
|
tm := time.Now()
|
||||||
|
// 计算从2000年开始到现在的索引值(2000年时间戳:946656000)(5位62进制最小值:14776336)(可以表示901356495个,即142.9年至2142年)
|
||||||
|
secIndex := (tm.Unix()-946656000)/5 + 14776336
|
||||||
|
secTag := int(tm.UnixMicro() % 5)
|
||||||
|
uid := u.AppendInt(nil, uint64(secIndex))
|
||||||
|
secIndexKey := fmt.Sprintf("_SecIdx_%d", secIndex)
|
||||||
|
inSecIndex := rd.INCR(secIndexKey)
|
||||||
|
if inSecIndex == 1 {
|
||||||
|
rd.EXPIRE(secIndexKey, 6)
|
||||||
|
}
|
||||||
|
inSecIndexBytes := u.EncodeInt(uint64(inSecIndex))
|
||||||
|
uid = u.AppendInt(uid, uint64(secTag*12+len(inSecIndexBytes))) // 长度位,防止不同长度+随机值发生碰撞(用毫秒的%4*12位基础+位数可以随机表示12位长度,看起来有变化)
|
||||||
|
uid = append(uid, inSecIndexBytes...)
|
||||||
|
|
||||||
|
// 用随机数填充
|
||||||
|
uid = u.FillInt(uid, size)
|
||||||
|
|
||||||
|
// 交叉乱序
|
||||||
|
uid = u.ExchangeInt(uid)
|
||||||
|
|
||||||
|
// 散列乱序
|
||||||
|
uid = u.HashInt(uid)
|
||||||
|
|
||||||
|
return vm.ToValue(string(uid))
|
||||||
},
|
},
|
||||||
|
// "id": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||||
|
// args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||||
|
// space := args.Str(0)
|
||||||
|
// size := args.Int(1)
|
||||||
|
// var id string
|
||||||
|
// if size >= 12 {
|
||||||
|
// id = s.Id12(space)
|
||||||
|
// } else if size >= 10 {
|
||||||
|
// id = s.Id10(space)
|
||||||
|
// } else if size >= 8 {
|
||||||
|
// id = s.Id8(space)
|
||||||
|
// } else {
|
||||||
|
// id = s.Id6(space)
|
||||||
|
// }
|
||||||
|
// return vm.ToValue(id)
|
||||||
|
// },
|
||||||
|
// "idL": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||||
|
// args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||||
|
// space := args.Str(0)
|
||||||
|
// size := args.Int(1)
|
||||||
|
// var id string
|
||||||
|
// if size >= 12 {
|
||||||
|
// id = s.Id12(space)
|
||||||
|
// } else if size >= 10 {
|
||||||
|
// id = s.Id10(space)
|
||||||
|
// } else if size >= 8 {
|
||||||
|
// id = s.Id8(space)
|
||||||
|
// } else {
|
||||||
|
// id = s.Id6(space)
|
||||||
|
// }
|
||||||
|
// return vm.ToValue(strings.ToLower(id))
|
||||||
|
// },
|
||||||
"register": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
"register": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||||
args := gojs.MakeArgs(&argsIn, vm).Check(2)
|
args := gojs.MakeArgs(&argsIn, vm).Check(2)
|
||||||
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 vm.ToValue(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
authLevel := o.Int("authLevel")
|
authLevel := o.Int("authLevel")
|
||||||
@ -402,7 +514,7 @@ func init() {
|
|||||||
for i, require := range requiresObj {
|
for i, require := range requiresObj {
|
||||||
requires[i] = u.String(require)
|
requires[i] = u.String(require)
|
||||||
}
|
}
|
||||||
vm.GoData[fmt.Sprint("REQUIRE_"+host, method, path)] = requires
|
vm.SetData(fmt.Sprint("REQUIRE_"+host, method, path), requires)
|
||||||
}
|
}
|
||||||
if verifiesObj := o.Obj("verifies"); verifiesObj != nil {
|
if verifiesObj := o.Obj("verifies"); verifiesObj != nil {
|
||||||
verifiesSet := map[string]func(any, *goja.Runtime) bool{}
|
verifiesSet := map[string]func(any, *goja.Runtime) bool{}
|
||||||
@ -436,29 +548,29 @@ func init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vm.GoData[fmt.Sprint("VERIFY_"+host, method, path)] = verifiesSet
|
vm.SetData(fmt.Sprint("VERIFY_"+host, method, path), verifiesSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
opt := s.WebServiceOptions{
|
opt := s.WebServiceOptions{
|
||||||
NoBody: o.Bool("noBody"),
|
NoBody: o.Bool("noBody"),
|
||||||
NoLog200: o.Bool("noLog200"),
|
NoLog200: o.Bool("noLog200"),
|
||||||
Host: host,
|
Host: host,
|
||||||
//Ext: nil,
|
Ext: o.Map("ext"),
|
||||||
Limiters: usedLimiters,
|
Limiters: usedLimiters,
|
||||||
}
|
}
|
||||||
|
|
||||||
startFile := u.String(vm.GoData["startFile"])
|
startFile := u.String(vm.GetData("startFile"))
|
||||||
poolsLock.RLock()
|
poolsLock.RLock()
|
||||||
poolExist := poolExists[startFile]
|
poolExist := poolExists[startFile]
|
||||||
poolsLock.RUnlock()
|
poolsLock.RUnlock()
|
||||||
if poolExist {
|
if poolExist {
|
||||||
// 从对象调用(支持并发)
|
// 从对象调用(支持并发)
|
||||||
actionKey := "REGISTER_" + host + method + path
|
actionKey := "REGISTER_" + host + method + path
|
||||||
vm.GoData[actionKey] = action
|
vm.SetData(actionKey, action)
|
||||||
vm.GoData[actionKey+"This"] = args.This
|
vm.SetData(actionKey+"This", args.This)
|
||||||
if method == "WS" {
|
if method == "WS" {
|
||||||
vm.GoData[actionKey+"onMessage"] = o.Func("onMessage")
|
vm.SetData(actionKey+"onMessage", o.Func("onMessage"))
|
||||||
vm.GoData[actionKey+"onClose"] = o.Func("onClose")
|
vm.SetData(actionKey+"onClose", o.Func("onClose"))
|
||||||
}
|
}
|
||||||
poolsLock.Lock()
|
poolsLock.Lock()
|
||||||
actionRegistered := poolActionRegistered[actionKey]
|
actionRegistered := poolActionRegistered[actionKey]
|
||||||
@ -477,7 +589,7 @@ func init() {
|
|||||||
// 无对象池,直接调用(单线程)
|
// 无对象池,直接调用(单线程)
|
||||||
s.RestfulWithOptions(authLevel, method, path, makeInnerAction(action, vm, args.This), memo, opt)
|
s.RestfulWithOptions(authLevel, method, path, makeInnerAction(action, vm, args.This), memo, opt)
|
||||||
}
|
}
|
||||||
return nil
|
return vm.ToValue(true)
|
||||||
},
|
},
|
||||||
"load": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
"load": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||||
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||||
@ -497,104 +609,146 @@ func init() {
|
|||||||
mainArgs = mainArgs1
|
mainArgs = mainArgs1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !u.FileExists(actionFile) {
|
fi := u.GetFileInfo(actionFile)
|
||||||
|
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 vm.ToValue(false)
|
||||||
}
|
}
|
||||||
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 and call service.register")))
|
// 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 vm.ToValue(false)
|
||||||
}
|
}
|
||||||
poolsLock.Lock()
|
poolsLock.Lock()
|
||||||
poolExists[actionFile] = true
|
poolExists[actionFile] = true
|
||||||
poolsLock.Unlock()
|
poolsLock.Unlock()
|
||||||
p := gojs.NewPoolByCode(actionCode, actionFile, gojs.PoolConfig{
|
poolOpt := gojs.PoolConfig{
|
||||||
Min: mi,
|
Min: mi,
|
||||||
Max: ma,
|
Max: ma,
|
||||||
Idle: idle,
|
Idle: idle,
|
||||||
Debug: debug,
|
Debug: debug,
|
||||||
Args: mainArgs,
|
Args: mainArgs,
|
||||||
}, args.Logger)
|
}
|
||||||
|
p := gojs.NewPoolByCode(actionCode, actionFile, poolOpt, args.Logger)
|
||||||
//p := gojs.NewLBByCode(actionCode, actionFile, gojs.LBConfig{
|
//p := gojs.NewLBByCode(actionCode, actionFile, gojs.LBConfig{
|
||||||
// Num: num,
|
// Num: num,
|
||||||
// Debug: debug,
|
// Debug: debug,
|
||||||
// Args: mainArgs,
|
// Args: mainArgs,
|
||||||
//}, args.Logger)
|
//}, args.Logger)
|
||||||
|
mtime := fi.ModTime.Unix()
|
||||||
poolsLock.Lock()
|
poolsLock.Lock()
|
||||||
pools[actionFile] = p
|
pools[actionFile] = p
|
||||||
|
poolsMTime[actionFile] = mtime
|
||||||
|
poolsConfig[actionFile] = poolOpt
|
||||||
poolsLock.Unlock()
|
poolsLock.Unlock()
|
||||||
return nil
|
return vm.ToValue(true)
|
||||||
},
|
},
|
||||||
"task": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
// "task": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||||
|
// args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||||
|
// taskFile := args.Path(0)
|
||||||
|
// interval := args.Int(1)
|
||||||
|
// if interval == 0 {
|
||||||
|
// interval = 1000
|
||||||
|
// }
|
||||||
|
// if interval < 100 {
|
||||||
|
// interval = 100
|
||||||
|
// }
|
||||||
|
// if !u.FileExists(taskFile) {
|
||||||
|
// panic(vm.NewGoError(errors.New("taskFile must be a js file path")))
|
||||||
|
// }
|
||||||
|
// rt := gojs.New()
|
||||||
|
// _, err := rt.RunFile(taskFile)
|
||||||
|
// if err != nil {
|
||||||
|
// panic(vm.NewGoError(err))
|
||||||
|
// }
|
||||||
|
// s.NewTimerServer(taskFile, time.Duration(interval)*time.Millisecond, func(isRunning *bool) {
|
||||||
|
// rt.RunCode("if(onRun)onRun()")
|
||||||
|
// }, func() {
|
||||||
|
// rt.RunCode("if(onStart)onStart()")
|
||||||
|
// }, func() {
|
||||||
|
// rt.RunCode("if(onStop)onStop()")
|
||||||
|
// })
|
||||||
|
// return nil
|
||||||
|
// },
|
||||||
|
// "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)
|
||||||
taskFile := args.Path(0)
|
replaces := args.Map(0)
|
||||||
interval := args.Int(1)
|
tplLock.Lock()
|
||||||
if interval == 0 {
|
for k, v := range replaces {
|
||||||
interval = 1000
|
tplReplaces[k] = u.String(v)
|
||||||
}
|
|
||||||
if interval < 100 {
|
|
||||||
interval = 100
|
|
||||||
}
|
|
||||||
if !u.FileExists(taskFile) {
|
|
||||||
panic(vm.NewGoError(errors.New("taskFile must be a js file path")))
|
|
||||||
}
|
|
||||||
rt := gojs.New()
|
|
||||||
_, err := rt.RunFile(taskFile)
|
|
||||||
if err != nil {
|
|
||||||
panic(vm.NewGoError(err))
|
|
||||||
}
|
|
||||||
s.NewTimerServer(taskFile, time.Duration(interval)*time.Millisecond, func(isRunning *bool) {
|
|
||||||
rt.RunCode("if(onRun)onRun()")
|
|
||||||
}, func() {
|
|
||||||
rt.RunCode("if(onStart)onStart()")
|
|
||||||
}, func() {
|
|
||||||
rt.RunCode("if(onStop)onStop()")
|
|
||||||
})
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
"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])
|
|
||||||
}
|
|
||||||
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)
|
||||||
@ -605,58 +759,124 @@ 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())
|
||||||
},
|
},
|
||||||
"dataSet": DataSet,
|
// "dataSet": DataSet,
|
||||||
"dataGet": DataGet,
|
// "dataGet": DataGet,
|
||||||
"dataKeys": DataKeys,
|
// "dataKeys": DataKeys,
|
||||||
"dataCount": DataCount,
|
// "dataCount": DataCount,
|
||||||
"dataFetch": DataFetch,
|
// "dataFetch": DataFetch,
|
||||||
"dataRemove": DataRemove,
|
// "dataRemove": DataRemove,
|
||||||
"listPop": ListPop,
|
// "listPop": ListPop,
|
||||||
"listPush": ListPush,
|
// "listPush": ListPush,
|
||||||
"listCount": ListCount,
|
// "listCount": ListCount,
|
||||||
"listRemove": ListRemove,
|
// "listRemove": ListRemove,
|
||||||
"newCaller": NewCaller,
|
"newCaller": NewCaller,
|
||||||
}
|
}
|
||||||
|
|
||||||
gojs.Register("apigo.cc/gojs/service", gojs.Module{
|
gojs.Register("apigo.cc/gojs/service", gojs.Module{
|
||||||
@ -682,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))
|
||||||
}
|
}
|
||||||
@ -771,10 +1034,10 @@ func runAction(action goja.Callable, vm *goja.Runtime, thisArg goja.Value, args
|
|||||||
defer vm.CallbackLocker.Unlock()
|
defer vm.CallbackLocker.Unlock()
|
||||||
|
|
||||||
// 验证请求参数的有效性
|
// 验证请求参数的有效性
|
||||||
if verifies, ok := vm.GoData["VERIFY_"+u.String(request.Get("registerTag"))].(map[string]func(any, *goja.Runtime) bool); ok {
|
if verifies, ok := vm.GetData("VERIFY_" + u.String(request.Get("registerTag"))).(map[string]func(any, *goja.Runtime) bool); ok {
|
||||||
failedFields := make([]string, 0)
|
failedFields := make([]string, 0)
|
||||||
// 检查必填字段
|
// 检查必填字段
|
||||||
if requires, ok := vm.GoData["REQUIRE_"+u.String(request.Get("registerTag"))].([]string); ok {
|
if requires, ok := vm.GetData("REQUIRE_" + u.String(request.Get("registerTag"))).([]string); ok {
|
||||||
for _, requireField := range requires {
|
for _, requireField := range requires {
|
||||||
if _, ok := args[requireField]; !ok {
|
if _, ok := args[requireField]; !ok {
|
||||||
failedFields = append(failedFields, requireField)
|
failedFields = append(failedFields, requireField)
|
||||||
@ -805,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
|
||||||
|
|
||||||
|
|||||||
482
service.ts
482
service.ts
@ -1,29 +1,30 @@
|
|||||||
// just for develop
|
// just for develop
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
config,
|
config,
|
||||||
start,
|
start,
|
||||||
stop,
|
stop,
|
||||||
register,
|
register,
|
||||||
load,
|
load,
|
||||||
task,
|
// task,
|
||||||
newCaller,
|
newCaller,
|
||||||
dataSet,
|
// dataSet,
|
||||||
dataGet,
|
// dataGet,
|
||||||
dataKeys,
|
// dataKeys,
|
||||||
dataCount,
|
// dataCount,
|
||||||
dataFetch,
|
// dataFetch,
|
||||||
dataRemove,
|
// dataRemove,
|
||||||
listPush,
|
// listPush,
|
||||||
listPop,
|
// listPop,
|
||||||
listCount,
|
// listCount,
|
||||||
listRemove,
|
// listRemove,
|
||||||
id,
|
id,
|
||||||
idL,
|
// idL,
|
||||||
uniqueId,
|
// uniqueId,
|
||||||
uniqueIdL,
|
// uniqueIdL,
|
||||||
setTplFunc,
|
// setTplFunc,
|
||||||
tpl,
|
setTplReplaces,
|
||||||
|
tpl,
|
||||||
}
|
}
|
||||||
|
|
||||||
function config(config?: Config): void { }
|
function config(config?: Config): void { }
|
||||||
@ -32,259 +33,264 @@ function stop(): void { }
|
|||||||
function register(option: RegisterOption, callback: (params: RequestParams) => void): any { return null }
|
function register(option: RegisterOption, callback: (params: RequestParams) => void): any { return null }
|
||||||
function load(serviceFile: string, poolConfig?: PoolConfig): void { }
|
function load(serviceFile: string, poolConfig?: PoolConfig): void { }
|
||||||
|
|
||||||
function task(taskFile: string, interval: number = 1000): void { }
|
// function task(taskFile: string, interval: number = 1000): void { }
|
||||||
|
|
||||||
function newCaller(): Caller { return null as any }
|
function newCaller(): Caller { return null as any }
|
||||||
|
|
||||||
function dataSet(scope: string, key: string, value: any): void { }
|
// function dataSet(scope: string, key: string, value: any): void { }
|
||||||
function dataGet(scope: string, key: string): any { return null }
|
// function dataGet(scope: string, key: string): any { return null }
|
||||||
function dataKeys(scope: string): string[] { return [] }
|
// function dataKeys(scope: string): string[] { return [] }
|
||||||
function dataCount(scope: string): number { return 0 }
|
// function dataCount(scope: string): number { return 0 }
|
||||||
function dataFetch(scope: string): Map<string, any> { return null as any }
|
// function dataFetch(scope: string): Map<string, any> { return null as any }
|
||||||
function dataRemove(scope: string, key?: string): void { }
|
// function dataRemove(scope: string, key?: string): void { }
|
||||||
|
|
||||||
function listPush(scope: string, key: string, value: any): void { }
|
// function listPush(scope: string, key: string, value: any): void { }
|
||||||
function listPop(scope: string, key: string): any { return null }
|
// function listPop(scope: string, key: string): any { return null }
|
||||||
function listCount(scope: string): number { return 0 }
|
// function listCount(scope: string): number { return 0 }
|
||||||
function listRemove(scope: string): void { }
|
// function listRemove(scope: string): void { }
|
||||||
function id(space: string, size?: number): string { return '' }
|
function id(size?: number): string { return '' }
|
||||||
function idL(space: string, size?: number): string { return '' }
|
// function id(space: string, size?: number): string { return '' }
|
||||||
function uniqueId(size?: number): string { return '' }
|
// function idL(space: string, size?: number): string { return '' }
|
||||||
function uniqueIdL(size?: number): string { return '' }
|
// function uniqueId(size?: number): string { return '' }
|
||||||
function setTplFunc(fnList: Object): void { }
|
// function uniqueIdL(size?: number): string { return '' }
|
||||||
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 '' }
|
||||||
|
|
||||||
interface Config {
|
export interface Config {
|
||||||
// github.com/ssgo/s 的配置参数
|
// github.com/ssgo/s 的配置参数
|
||||||
listen: string // 监听端口(|隔开多个监听)(,隔开多个选项)(如果不指定IP则监听在0.0.0.0,如果不指定端口则使用h2c协议监听在随机端口,80端口默认使用http协议,443端口默认使用https协议),例如 80,http|443|443:h2|127.0.0.1:8080,h2c
|
listen: string // 监听端口(|隔开多个监听)(,隔开多个选项)(如果不指定IP则监听在0.0.0.0,如果不指定端口则使用h2c协议监听在随机端口,80端口默认使用http协议,443端口默认使用https协议),例如 80,http|443|443:h2|127.0.0.1:8080,h2c
|
||||||
ssl: Map<string, CertSet> // SSL证书配置,key为域名,value为cert和key的文件路径
|
ssl: Map<string, CertSet> // SSL证书配置,key为域名,value为cert和key的文件路径
|
||||||
noLogGets: boolean // 不记录GET请求的日志
|
noLogGets: boolean // 不记录GET请求的日志
|
||||||
noLogHeaders: string // 不记录请求头中包含的这些字段,多个字段用逗号分隔,默认不记录:Accept,Accept-Encoding,Cache-Control,Pragma,Connection,Upgrade-Insecure-Requests
|
noLogHeaders: string // 不记录请求头中包含的这些字段,多个字段用逗号分隔,默认不记录:Accept,Accept-Encoding,Cache-Control,Pragma,Connection,Upgrade-Insecure-Requests
|
||||||
logInputArrayNum: number // 请求字段中容器类型(数组、Map)在日志打印个数限制 默认为10个,多余的数据将不再日志中记录
|
logInputArrayNum: number // 请求字段中容器类型(数组、Map)在日志打印个数限制 默认为10个,多余的数据将不再日志中记录
|
||||||
logInputFieldSize: number // 请求字段中单个字段在日志打印长度限制 默认为500个字符,多余的数据将不再日志中记录
|
logInputFieldSize: number // 请求字段中单个字段在日志打印长度限制 默认为500个字符,多余的数据将不再日志中记录
|
||||||
noLogOutputFields: string // 不记录响应字段中包含的这些字段(key名),多个字段用逗号分隔
|
noLogOutputFields: string // 不记录响应字段中包含的这些字段(key名),多个字段用逗号分隔
|
||||||
logOutputArrayNum: number // 响应字段中容器类型(数组、Map)在日志打印个数限制 默认为3个,多余的数据将不再日志中记录
|
logOutputArrayNum: number // 响应字段中容器类型(数组、Map)在日志打印个数限制 默认为3个,多余的数据将不再日志中记录
|
||||||
logOutputFieldSize: number // 响应字段中单个字段在日志打印长度限制 默认为100个字符,多余的数据将不再日志中记录
|
logOutputFieldSize: number // 响应字段中单个字段在日志打印长度限制 默认为100个字符,多余的数据将不再日志中记录
|
||||||
logWebsocketAction: boolean // 记录Websocket中每个Action的请求日志,默认不记录
|
logWebsocketAction: boolean // 记录Websocket中每个Action的请求日志,默认不记录
|
||||||
compress: boolean // 是否启用压缩,默认不启用
|
compress: boolean // 是否启用压缩,默认不启用
|
||||||
compressMinSize: number // 小于设定值的应答内容将不进行压缩,默认值:1024
|
compressMinSize: number // 小于设定值的应答内容将不进行压缩,默认值:1024
|
||||||
compressMaxSize: number // 大于设定值的应答内容将不进行压缩,默认值:4096000
|
compressMaxSize: number // 大于设定值的应答内容将不进行压缩,默认值:4096000
|
||||||
checkDomain: string // 心跳检测时使用域名,,默认使用IP地址,心跳检测使用 HEAD /__CHECK__ 请求,应答 299 表示正常,593 表示异常
|
checkDomain: string // 心跳检测时使用域名,,默认使用IP地址,心跳检测使用 HEAD /__CHECK__ 请求,应答 299 表示正常,593 表示异常
|
||||||
redirectTimeout: number // proxy和discover发起请求时的超时时间,单位ms,默认值:10000
|
redirectTimeout: number // proxy和discover发起请求时的超时时间,单位ms,默认值:10000
|
||||||
acceptXRealIpWithoutRequestId: boolean // 是否允许头部没有携带请求ID的X-Real-IP信息,默认不允许(防止伪造客户端IP)
|
acceptXRealIpWithoutRequestId: boolean // 是否允许头部没有携带请求ID的X-Real-IP信息,默认不允许(防止伪造客户端IP)
|
||||||
statisticTime: boolean // 是否开启请求时间统计,默认不开启
|
statisticTime: boolean // 是否开启请求时间统计,默认不开启
|
||||||
statisticTimeInterval: number // 统计时间间隔,单位ms,默认值:10000
|
statisticTimeInterval: number // 统计时间间隔,单位ms,默认值:10000
|
||||||
fast: boolean // 是否启用快速模式(为了追求性能牺牲一部分特性),默认不启用
|
fast: boolean // 是否启用快速模式(为了追求性能牺牲一部分特性),默认不启用
|
||||||
maxUploadSize: number // 最大上传文件大小(multipart/form-data请求的总空间),单位字节,默认值:104857600
|
maxUploadSize: number // 最大上传文件大小(multipart/form-data请求的总空间),单位字节,默认值:104857600
|
||||||
cpu: number // CPU占用的核数,默认为0,即不做限制
|
cpu: number // CPU占用的核数,默认为0,即不做限制
|
||||||
memory: number // 内存(单位M),默认为0,即不做限制
|
memory: number // 内存(单位M),默认为0,即不做限制
|
||||||
cpuMonitor: boolean // 在日志中记录CPU使用情况,默认不开启
|
cpuMonitor: boolean // 在日志中记录CPU使用情况,默认不开启
|
||||||
memoryMonitor: boolean // 在日志中记录内存使用情况,默认不开启
|
memoryMonitor: boolean // 在日志中记录内存使用情况,默认不开启
|
||||||
cpuLimitValue: number // CPU超过最高占用值(10-100)超过次数将自动重启(如果CpuMonitor开启的话),默认100
|
cpuLimitValue: number // CPU超过最高占用值(10-100)超过次数将自动重启(如果CpuMonitor开启的话),默认100
|
||||||
memoryLimitValue: number // 内存超过最高占用值(10-100)超过次数将自动重启(如果MemoryMonitor开启的话),默认95
|
memoryLimitValue: number // 内存超过最高占用值(10-100)超过次数将自动重启(如果MemoryMonitor开启的话),默认95
|
||||||
cpuLimitTimes: number // CPU超过最高占用值超过次数(1-100)将报警(如果CpuMonitor开启的话),默认6(即30秒内连续6次)
|
cpuLimitTimes: number // CPU超过最高占用值超过次数(1-100)将报警(如果CpuMonitor开启的话),默认6(即30秒内连续6次)
|
||||||
memoryLimitTimes: number // 内存超过最高占用值超过次数(1-100)将报警(如果MemoryMonitor开启的话),默认6(即30秒内连续6次)
|
memoryLimitTimes: number // 内存超过最高占用值超过次数(1-100)将报警(如果MemoryMonitor开启的话),默认6(即30秒内连续6次)
|
||||||
cookieScope: string // 启用Session时Cookie的有效范围,host|domain|topDomain,默认值为host
|
cookieScope: string // 启用Session时Cookie的有效范围,host|domain|topDomain,默认值为host
|
||||||
sessionWithoutCookie: boolean // Session禁用Cookie保持,默认使用Cookie
|
sessionWithoutCookie: boolean // Session禁用Cookie保持,默认使用Cookie
|
||||||
deviceWithoutCookie: boolean // 设备ID禁用Cookie保持,默认使用Cookie
|
deviceWithoutCookie: boolean // 设备ID禁用Cookie保持,默认使用Cookie
|
||||||
idServer: string // 用s.UniqueId、s.Id来生成唯一ID(雪花算法)时所需的redis服务器连接,如果不指定将不能实现跨服务的全局唯一
|
idServer: string // 用s.UniqueId、s.Id来生成唯一ID(雪花算法)时所需的redis服务器连接,如果不指定将不能实现跨服务的全局唯一
|
||||||
keepKeyCase: boolean // 是否保持Key的首字母大小写?默认保持,设置为false则自动将首字母转为小写
|
keepKeyCase: boolean // 是否保持Key的首字母大小写?默认保持,设置为false则自动将首字母转为小写
|
||||||
indexFiles: string[] // 访问静态文件时的索引文件,默认为 index.html
|
indexFiles: string[] // 访问静态文件时的索引文件,默认为 index.html
|
||||||
indexDir: boolean // 访问目录时显示文件列表
|
indexDir: boolean // 访问目录时显示文件列表
|
||||||
readTimeout: number // 读取请求的超时时间,单位ms
|
readTimeout: number // 读取请求的超时时间,单位ms
|
||||||
readHeaderTimeout: number // 读取请求头的超时时间,单位ms
|
readHeaderTimeout: number // 读取请求头的超时时间,单位ms
|
||||||
writeTimeout: number // 响应写入的超时时间,单位ms
|
writeTimeout: number // 响应写入的超时时间,单位ms
|
||||||
idleTimeout: number // 连接空闲超时时间,单位ms
|
idleTimeout: number // 连接空闲超时时间,单位ms
|
||||||
maxHeaderBytes: number // 请求头的最大字节数
|
maxHeaderBytes: number // 请求头的最大字节数
|
||||||
maxHandlers: number // 每个连接的最大处理程序数量
|
maxHandlers: number // 每个连接的最大处理程序数量
|
||||||
maxConcurrentStreams: number // 每个连接的最大并发流数量
|
maxConcurrentStreams: number // 每个连接的最大并发流数量
|
||||||
maxDecoderHeaderTableSize: number // 解码器头表的最大大小
|
maxDecoderHeaderTableSize: number // 解码器头表的最大大小
|
||||||
maxEncoderHeaderTableSize: number // 编码器头表的最大大小
|
maxEncoderHeaderTableSize: number // 编码器头表的最大大小
|
||||||
maxReadFrameSize: number // 单个帧的最大读取大小
|
maxReadFrameSize: number // 单个帧的最大读取大小
|
||||||
maxUploadBufferPerConnection: number // 每个连接的最大上传缓冲区大小
|
maxUploadBufferPerConnection: number // 每个连接的最大上传缓冲区大小
|
||||||
maxUploadBufferPerStream: number // 每个流的最大上传缓冲区大小
|
maxUploadBufferPerStream: number // 每个流的最大上传缓冲区大小
|
||||||
|
|
||||||
// 其他配置
|
// 其他配置
|
||||||
sessionKey: string // HTTP头和Cookie中SessionID的Key,客户端没有传递时服务端自动生成,Header的优先级高于Cookie,默认为 Session, 设置为空时表示不使用
|
sessionKey: string // HTTP头和Cookie中SessionID的Key,客户端没有传递时服务端自动生成,Header的优先级高于Cookie,默认为 Session, 设置为空时表示不使用
|
||||||
deviceKey: string // 标识设备ID的Key,客户端没有传递时服务端自动生成,Header的优先级高于Cookie,默认为 Device, 设置为空时表示不使用
|
deviceKey: string // 标识设备ID的Key,客户端没有传递时服务端自动生成,Header的优先级高于Cookie,默认为 Device, 设置为空时表示不使用
|
||||||
clientKey: string // 标识客户端的Key,默认为 Client,对应的Header头为 ClientName 和 ClientVersion
|
clientKey: string // 标识客户端的Key,默认为 Client,对应的Header头为 ClientName 和 ClientVersion
|
||||||
userIdKey: string // session中userID的Key,用于在日志中记录用户ID信息,默认为 id
|
userIdKey: string // session中userID的Key,用于在日志中记录用户ID信息,默认为 id
|
||||||
sessionProvider: string // 指定一个redis连接(例如:redis://:sskey加密的密码@127.0.0.1:6379/15),默认使用内存存储
|
sessionProvider: string // 指定一个redis连接(例如:redis://:sskey加密的密码@127.0.0.1:6379/15),默认使用内存存储
|
||||||
sessionTimeout: number // session过期时间,单位 秒,默认为 3600秒
|
sessionTimeout: number // session过期时间,单位 秒,默认为 3600秒
|
||||||
authFieldMessage: string | Object // 身份验证失败时的消息,默认为 auth failed,可以设置对象来返回JSON,可以使用模版 {{TARGET_AUTHLEVEL}}、{{USER_AUTHLEVEL}}
|
authFieldMessage: string | Object // 身份验证失败时的消息,默认为 auth failed,可以设置对象来返回JSON,可以使用模版 {{TARGET_AUTHLEVEL}}、{{USER_AUTHLEVEL}}
|
||||||
verifyFieldMessage: string | Object // 参数验证失败时的消息,默认为 verify failed,可以设置对象来返回JSON,可以使用模版 {{FAILED_FIELDS}}
|
verifyFieldMessage: string | Object // 参数验证失败时的消息,默认为 verify failed,可以设置对象来返回JSON,可以使用模版 {{FAILED_FIELDS}}
|
||||||
limitedMessage: string | Object // 访问受限时的消息,默认为 too many requests,可以设置对象来返回JSON,可以使用模版 {{LIMITED_FROM}}、{{LIMITED_VALUE}}
|
limitedMessage: string | Object // 访问受限时的消息,默认为 too many requests,可以设置对象来返回JSON,可以使用模版 {{LIMITED_FROM}}、{{LIMITED_VALUE}}
|
||||||
limiterRedis: string // 限流器使用的Redis连接,默认使用内存存储
|
limiterRedis: string // 限流器使用的Redis连接,默认使用内存存储
|
||||||
limiters: Map<string, LimiterConfig> // 限流器配置,from 为数据来源,例如:ip、user、device、header.User-Agent、in.phone 等(in表示从请求参数中获取),time为时间间隔,单位ms,times为 时间间隔内允许访问的次数
|
limiters: Map<string, LimiterConfig> // 限流器配置,from 为数据来源,例如:ip、user、device、header.User-Agent、in.phone 等(in表示从请求参数中获取),time为时间间隔,单位ms,times为 时间间隔内允许访问的次数
|
||||||
|
hotLoad: number // 热加载配置,单位s,默认值:0,0表示不检测热加载
|
||||||
|
tplSafePaths: string[] // 模板文件的安全路径,默认不开启安全检查
|
||||||
|
|
||||||
// gateway 的配置参数
|
// gateway 的配置参数
|
||||||
proxy: Map<string, string> // 代理配置,key为[host][path],value为代理的目标应用或URL
|
proxy: Map<string, string> // 代理配置,key为[host][path],value为代理的目标应用或URL
|
||||||
rewrite: Map<string, string> // 重写请求路径,key为[host][path],value为重写后的路径
|
rewrite: Map<string, string> // 重写请求路径,key为[host][path],value为重写后的路径
|
||||||
static: Map<string, string> // 静态文件目录,key为[host][path],value为目录路径
|
static: Map<string, string> // 静态文件目录,key为[host][path],value为目录路径
|
||||||
|
|
||||||
// github.com/ssgo/discover 的配置参数
|
// github.com/ssgo/discover 的配置参数
|
||||||
registry: string // 服务注册中心,请配置一个有效的RedisURL,默认值 redis://:@127.0.0.1:6379/15
|
registry: string // 服务注册中心,请配置一个有效的RedisURL,默认值 redis://:@127.0.0.1:6379/15
|
||||||
app: string // 设置该值将会自动将服务注册到 discover
|
app: string // 设置该值将会自动将服务注册到 discover
|
||||||
weight: number // 节点的权重,默认值 100
|
weight: number // 节点的权重,默认值 100
|
||||||
accessTokens: Map<string, number> // 请求接口时使用指定的Access-Token进行验证,值为Token对应的authLevel(允许访问大于等于指定对应authLevel的接口)
|
accessTokens: Map<string, number> // 请求接口时使用指定的Access-Token进行验证,值为Token对应的authLevel(允许访问大于等于指定对应authLevel的接口)
|
||||||
calls: Map<string, string> // 配置将会调用的服务,格式:[1|2]:[http|https|h2c]:[AccessToken],默认协议为:h2c,如使用h2c协议可只提供 AccessToken
|
calls: Map<string, string> // 配置将会调用的服务,格式:[1|2]:[http|https|h2c]:[AccessToken],默认协议为:h2c,如使用h2c协议可只提供 AccessToken
|
||||||
callRetryTimes: number // 调用其他服务时最大重试次数,默认值 10
|
callRetryTimes: number // 调用其他服务时最大重试次数,默认值 10
|
||||||
ipPrefix: string // discover服务发现时指定使用的IP网段,默认排除 172.17.(Docker)
|
ipPrefix: string // discover服务发现时指定使用的IP网段,默认排除 172.17.(Docker)
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CertSet {
|
export interface CertSet {
|
||||||
certFile: string
|
certFile: string
|
||||||
keyFile: string
|
keyFile: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PoolConfig {
|
export interface PoolConfig {
|
||||||
min: number
|
min: number
|
||||||
max: string
|
max: string
|
||||||
idle: number
|
idle: number
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LimiterConfig {
|
export interface LimiterConfig {
|
||||||
from: string
|
from: string
|
||||||
time: number
|
time: number
|
||||||
times: number
|
times: number
|
||||||
}
|
}
|
||||||
|
|
||||||
interface RegisterOption {
|
export interface RegisterOption {
|
||||||
authLevel: number
|
authLevel: number
|
||||||
host: string
|
host: string
|
||||||
method: string
|
method: string
|
||||||
path: string
|
path: string
|
||||||
memo: string
|
memo: string
|
||||||
noBody: boolean
|
noBody: boolean
|
||||||
noLog200: boolean
|
noLog200: boolean
|
||||||
limiters: string[]
|
limiters: string[]
|
||||||
verifies: Object
|
verifies: Object
|
||||||
requires: string[]
|
requires: string[]
|
||||||
onMessage: (params: OnMessageParams) => void
|
ext: Object
|
||||||
onClose: (params: RequestParams) => void
|
onMessage: (params: OnMessageParams) => void
|
||||||
|
onClose: (params: RequestParams) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
interface RequestParams {
|
export interface RequestParams {
|
||||||
args: Object
|
args: Object
|
||||||
headers: Object
|
headers: Object
|
||||||
request: Request
|
request: Request
|
||||||
client: WSClient
|
client: WSClient
|
||||||
caller: Caller
|
caller: Caller
|
||||||
session: Session
|
session: Session
|
||||||
response: Response
|
response: Response
|
||||||
logger: Logger
|
logger: Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OnMessageParams {
|
export interface OnMessageParams {
|
||||||
type: string
|
type: string
|
||||||
data: string | Object
|
data: string | Object
|
||||||
client: WSClient
|
client: WSClient
|
||||||
session: Session
|
session: Session
|
||||||
logger: Logger
|
logger: Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
interface WSClient {
|
export interface WSClient {
|
||||||
id: string
|
id: string
|
||||||
read: () => WSMessage
|
read: () => WSMessage
|
||||||
write: (data: any) => void
|
write: (data: any) => void
|
||||||
writeMessage: (type: string, data: any) => void
|
writeMessage: (type: string, data: any) => void
|
||||||
ping: () => void
|
ping: () => void
|
||||||
close: () => void
|
close: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
interface WSMessage {
|
export interface WSMessage {
|
||||||
type: string
|
type: string
|
||||||
data: string | Object
|
data: string | Object
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Session {
|
export interface Session {
|
||||||
set: (key: string | Object, value?: any) => void
|
set: (key: string | Object, value?: any) => void
|
||||||
get: (...keys: string[]) => any | Object
|
get: (...keys: string[]) => any | Object
|
||||||
remove: (...keys: string[]) => void
|
remove: (...keys: string[]) => void
|
||||||
setAuthLevel: (authLevel: number) => void
|
setAuthLevel: (authLevel: number) => void
|
||||||
save: () => void
|
save: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Caller {
|
export interface Caller {
|
||||||
get(url: string, headers?: Object): Result
|
get(url: string, headers?: Object): Result
|
||||||
head(url: string, headers?: Object): Result
|
head(url: string, headers?: Object): Result
|
||||||
post(url: string, data: any, headers?: Object): Result
|
post(url: string, data: any, headers?: Object): Result
|
||||||
put(url: string, data: any, headers?: Object): Result
|
put(url: string, data: any, headers?: Object): Result
|
||||||
delete(url: string, data: any, headers?: Object): Result
|
delete(url: string, data: any, headers?: Object): Result
|
||||||
do(method: string, url: string, data: any, callback?: (data: string) => void, headers?: Object): Result
|
do(method: string, url: string, data: any, callback?: (data: string) => void, headers?: Object): Result
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Result {
|
export interface Result {
|
||||||
status: string
|
status: string
|
||||||
statusCode: number
|
statusCode: number
|
||||||
headers: Object
|
headers: Object
|
||||||
bytes(): Uint8Array
|
bytes(): Uint8Array
|
||||||
string(): string
|
string(): string
|
||||||
object(): Object
|
object(): Object
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CookieOption {
|
export interface CookieOption {
|
||||||
path: string
|
path: string
|
||||||
domain: string
|
domain: string
|
||||||
expires: any
|
expires: any
|
||||||
maxAge: number
|
maxAge: number
|
||||||
secure: boolean
|
secure: boolean
|
||||||
httpOnly: boolean
|
httpOnly: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Request {
|
export interface Request {
|
||||||
id: string
|
id: string
|
||||||
proto: string
|
proto: string
|
||||||
scheme: string
|
scheme: string
|
||||||
host: string
|
host: string
|
||||||
method: string
|
method: string
|
||||||
path: string
|
path: string
|
||||||
remoteAddr: string
|
remoteAddr: string
|
||||||
realIP: string
|
realIP: string
|
||||||
referer: string
|
referer: string
|
||||||
userAgent: string
|
userAgent: string
|
||||||
url: string
|
url: string
|
||||||
contentLength: number
|
contentLength: number
|
||||||
cookies: Object
|
cookies: Object
|
||||||
headers: Object
|
headers: Object
|
||||||
args: Object
|
args: Object
|
||||||
files: Map<string, UploadFile>
|
files: Map<string, UploadFile>
|
||||||
multiFiles: Map<string, UploadFile[]>
|
multiFiles: Map<string, UploadFile[]>
|
||||||
makeURL: (path: string) => string
|
makeURL: (path: string) => string
|
||||||
readAll: () => any
|
readAll: () => any
|
||||||
read: (size: number) => any
|
read: (size: number) => any
|
||||||
close: () => void
|
close: () => void
|
||||||
get: (key: string) => any
|
get: (key: string) => any
|
||||||
set: (key: string, value: any) => void
|
set: (key: string, value: any) => void
|
||||||
getHeader: (key: string) => string
|
getHeader: (key: string) => string
|
||||||
setHeader: (key: string, value: string) => void
|
setHeader: (key: string, value: string) => void
|
||||||
setUserID: (id: string) => void
|
setUserID: (id: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Response {
|
export interface Response {
|
||||||
id: string
|
id: string
|
||||||
setStatus: (code: number) => void
|
setStatus: (code: number) => void
|
||||||
setCookie: (name: string, value: string, option?: CookieOption) => void
|
setCookie: (name: string, value: string, option?: CookieOption) => void
|
||||||
setHeader: (name: string, value: string) => void
|
setHeader: (name: string, value: string) => void
|
||||||
addHeader: (name: string, value: string) => void
|
addHeader: (name: string, value: string) => void
|
||||||
getHeader: (name: string) => string
|
getHeader: (name: string) => string
|
||||||
write: (data: any) => number
|
write: (data: any) => number
|
||||||
flush: () => void
|
flush: () => void
|
||||||
sendFile: (contentType: string, filename: string) => void
|
sendFile: (contentType: string, filename: string) => void
|
||||||
downloadFile: (contentType: string, filename: string, data: any) => void
|
downloadFile: (contentType: string, filename: string, data: any) => void
|
||||||
location: (url: string) => void
|
location: (url: string) => void
|
||||||
end: (data: any) => void
|
end: (data: any) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Logger {
|
export interface Logger {
|
||||||
debug: (message: string, info?: Object) => void
|
debug: (message: string, info?: Object) => void
|
||||||
info: (message: string, info?: Object) => void
|
info: (message: string, info?: Object) => void
|
||||||
warn: (message: string, info?: Object) => void
|
warn: (message: string, info?: Object) => void
|
||||||
error: (message: string, info?: Object) => void
|
error: (message: string, info?: Object) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UploadFile {
|
export interface UploadFile {
|
||||||
name: string
|
name: string
|
||||||
size: number
|
size: number
|
||||||
data: any
|
data: any
|
||||||
}
|
}
|
||||||
|
|||||||
83
session.go
83
session.go
@ -2,6 +2,7 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -9,12 +10,14 @@ import (
|
|||||||
"apigo.cc/gojs/goja"
|
"apigo.cc/gojs/goja"
|
||||||
"github.com/ssgo/log"
|
"github.com/ssgo/log"
|
||||||
"github.com/ssgo/redis"
|
"github.com/ssgo/redis"
|
||||||
|
"github.com/ssgo/u"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Session struct {
|
type Session struct {
|
||||||
id string
|
id string
|
||||||
conn *redis.Redis
|
conn *redis.Redis
|
||||||
data map[string]any
|
data map[string]any
|
||||||
|
funcAuthCache map[string]bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var sessionRedis *redis.Redis
|
var sessionRedis *redis.Redis
|
||||||
@ -40,9 +43,10 @@ func NewSession(id string, logger *log.Logger) *Session {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &Session{
|
return &Session{
|
||||||
id: id,
|
id: id,
|
||||||
conn: conn,
|
conn: conn,
|
||||||
data: data,
|
data: data,
|
||||||
|
funcAuthCache: map[string]bool{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,6 +106,73 @@ func (session *Session) SetAuthLevel(argsIn goja.FunctionCall, vm *goja.Runtime)
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (session *Session) authFuncs(needFuncs []string) bool {
|
||||||
|
cacheKey := strings.Join(needFuncs, "; ")
|
||||||
|
if cachedResult, ok := session.funcAuthCache[cacheKey]; ok {
|
||||||
|
return cachedResult
|
||||||
|
}
|
||||||
|
|
||||||
|
normalAuthOk := 0
|
||||||
|
requiredAuthTotal := 0
|
||||||
|
requiredAuthOk := 0
|
||||||
|
isOk := false
|
||||||
|
if userFuncs, ok := session.data["funcs"].([]string); ok && len(userFuncs) > 0 {
|
||||||
|
if u.StringIn(userFuncs, "system.superAdmin.") {
|
||||||
|
isOk = true
|
||||||
|
} else {
|
||||||
|
// 统计必须有几个权限
|
||||||
|
for _, needFunc := range needFuncs {
|
||||||
|
if needFunc[0] == '&' {
|
||||||
|
requiredAuthTotal++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否有匹配的权限(左匹配)
|
||||||
|
for _, needFunc := range needFuncs {
|
||||||
|
isRequired := false
|
||||||
|
if needFunc[0] == '&' {
|
||||||
|
// 必须有此权限
|
||||||
|
isRequired = true
|
||||||
|
needFunc = needFunc[1:]
|
||||||
|
}
|
||||||
|
for _, userFunc := range userFuncs {
|
||||||
|
if strings.HasPrefix(userFunc, needFunc) {
|
||||||
|
// 匹配成功
|
||||||
|
if isRequired {
|
||||||
|
// 必须有此权限并且匹配成功
|
||||||
|
requiredAuthOk++
|
||||||
|
} else {
|
||||||
|
// 普通权限匹配成功
|
||||||
|
normalAuthOk++
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if normalAuthOk > 0 && requiredAuthOk == requiredAuthTotal {
|
||||||
|
// 普通权限匹配成功,并且必须有权限也匹配成功,直接返回
|
||||||
|
isOk = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
session.funcAuthCache[cacheKey] = isOk
|
||||||
|
return isOk
|
||||||
|
}
|
||||||
|
|
||||||
|
func (session *Session) AuthFuncs(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||||
|
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||||
|
var needFuncs []string
|
||||||
|
if args.Arguments[0].ExportType().Kind() == reflect.Slice {
|
||||||
|
// 第一个参数是数组
|
||||||
|
needFuncs = args.Arr(0).StrArray(0)
|
||||||
|
} else {
|
||||||
|
// 平铺的参数
|
||||||
|
needFuncs = args.StrArray(0)
|
||||||
|
}
|
||||||
|
return vm.ToValue(session.authFuncs(needFuncs))
|
||||||
|
}
|
||||||
|
|
||||||
func (session *Session) Save(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
func (session *Session) Save(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||||
if session.conn == nil {
|
if session.conn == nil {
|
||||||
now := time.Now().Unix()
|
now := time.Now().Unix()
|
||||||
|
|||||||
299
task.go
299
task.go
@ -1,162 +1,165 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
// type Task struct {
|
||||||
"container/list"
|
// spec string
|
||||||
"sync"
|
// file string
|
||||||
|
// vm *gojs.Runtime
|
||||||
|
// lock sync.RWMutex
|
||||||
|
// mtime time.Time
|
||||||
|
// // policy string
|
||||||
|
// }
|
||||||
|
// var tasks []Task
|
||||||
|
// var tasksLock = sync.RWMutex{}
|
||||||
|
|
||||||
"apigo.cc/gojs"
|
// var taskData = map[string]map[string]any{}
|
||||||
"apigo.cc/gojs/goja"
|
// var taskDataLock = sync.RWMutex{}
|
||||||
)
|
|
||||||
|
|
||||||
var taskData = map[string]map[string]any{}
|
// var taskList = map[string]*list.List{}
|
||||||
var taskDataLock = sync.RWMutex{}
|
// var taskListLock = sync.RWMutex{}
|
||||||
|
|
||||||
var taskList = map[string]*list.List{}
|
// func DataSet(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||||
var taskListLock = sync.RWMutex{}
|
// args := gojs.MakeArgs(&argsIn, vm).Check(3)
|
||||||
|
// scope := args.Str(0)
|
||||||
|
// key := args.Str(1)
|
||||||
|
// value := args.Any(2)
|
||||||
|
// taskDataLock.Lock()
|
||||||
|
// defer taskDataLock.Unlock()
|
||||||
|
// if taskData[scope] == nil {
|
||||||
|
// taskData[scope] = map[string]any{}
|
||||||
|
// }
|
||||||
|
// taskData[scope][key] = value
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
|
||||||
func DataSet(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
// func DataGet(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||||
args := gojs.MakeArgs(&argsIn, vm).Check(3)
|
// args := gojs.MakeArgs(&argsIn, vm).Check(2)
|
||||||
scope := args.Str(0)
|
// scope := args.Str(0)
|
||||||
key := args.Str(1)
|
// key := args.Str(1)
|
||||||
value := args.Any(2)
|
// taskDataLock.RLock()
|
||||||
taskDataLock.Lock()
|
// defer taskDataLock.RUnlock()
|
||||||
defer taskDataLock.Unlock()
|
// if taskData[scope] != nil {
|
||||||
if taskData[scope] == nil {
|
// return vm.ToValue(taskData[scope][key])
|
||||||
taskData[scope] = map[string]any{}
|
// }
|
||||||
}
|
// return nil
|
||||||
taskData[scope][key] = value
|
// }
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func DataGet(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
// func DataKeys(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||||
args := gojs.MakeArgs(&argsIn, vm).Check(2)
|
// args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||||
scope := args.Str(0)
|
// scope := args.Str(0)
|
||||||
key := args.Str(1)
|
// taskDataLock.RLock()
|
||||||
taskDataLock.RLock()
|
// defer taskDataLock.RUnlock()
|
||||||
defer taskDataLock.RUnlock()
|
// if taskData[scope] != nil {
|
||||||
if taskData[scope] != nil {
|
// keys := make([]string, len(taskData[scope]))
|
||||||
return vm.ToValue(taskData[scope][key])
|
// i := 0
|
||||||
}
|
// for key := range taskData[scope] {
|
||||||
return nil
|
// keys[i] = key
|
||||||
}
|
// i++
|
||||||
|
// }
|
||||||
|
// return vm.ToValue(keys)
|
||||||
|
// }
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
|
||||||
func DataKeys(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
// func DataCount(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||||
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
// args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||||
scope := args.Str(0)
|
// scope := args.Str(0)
|
||||||
taskDataLock.RLock()
|
// taskDataLock.RLock()
|
||||||
defer taskDataLock.RUnlock()
|
// defer taskDataLock.RUnlock()
|
||||||
if taskData[scope] != nil {
|
// if taskData[scope] != nil {
|
||||||
keys := make([]string, len(taskData[scope]))
|
// return vm.ToValue(len(taskData[scope]))
|
||||||
i := 0
|
// }
|
||||||
for key := range taskData[scope] {
|
// return vm.ToValue(0)
|
||||||
keys[i] = key
|
// }
|
||||||
i++
|
|
||||||
}
|
|
||||||
return vm.ToValue(keys)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func DataCount(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
// func DataFetch(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||||
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
// args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||||
scope := args.Str(0)
|
// scope := args.Str(0)
|
||||||
taskDataLock.RLock()
|
// taskDataLock.RLock()
|
||||||
defer taskDataLock.RUnlock()
|
// defer taskDataLock.RUnlock()
|
||||||
if taskData[scope] != nil {
|
// if taskData[scope] != nil {
|
||||||
return vm.ToValue(len(taskData[scope]))
|
// all := make(map[string]any)
|
||||||
}
|
// for k, v := range taskData[scope] {
|
||||||
return vm.ToValue(0)
|
// all[k] = v
|
||||||
}
|
// }
|
||||||
|
// return vm.ToValue(all)
|
||||||
|
// }
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
|
||||||
func DataFetch(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
// func DataRemove(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||||
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
// args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||||
scope := args.Str(0)
|
// scope := args.Str(0)
|
||||||
taskDataLock.RLock()
|
// key := args.Str(1)
|
||||||
defer taskDataLock.RUnlock()
|
// taskDataLock.Lock()
|
||||||
if taskData[scope] != nil {
|
// defer taskDataLock.Unlock()
|
||||||
all := make(map[string]any)
|
// if taskData[scope] != nil {
|
||||||
for k, v := range taskData[scope] {
|
// if key != "" {
|
||||||
all[k] = v
|
// delete(taskData[scope], key)
|
||||||
}
|
// } else {
|
||||||
return vm.ToValue(all)
|
// delete(taskData, scope)
|
||||||
}
|
// }
|
||||||
return nil
|
// }
|
||||||
}
|
// return nil
|
||||||
|
// }
|
||||||
|
|
||||||
func DataRemove(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
// func ListPush(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||||
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
// args := gojs.MakeArgs(&argsIn, vm).Check(2)
|
||||||
scope := args.Str(0)
|
// scope := args.Str(0)
|
||||||
key := args.Str(1)
|
// value := args.Any(1)
|
||||||
taskDataLock.Lock()
|
// fromHead := args.Bool(2)
|
||||||
defer taskDataLock.Unlock()
|
// taskListLock.Lock()
|
||||||
if taskData[scope] != nil {
|
// defer taskListLock.Unlock()
|
||||||
if key != "" {
|
// list1 := taskList[scope]
|
||||||
delete(taskData[scope], key)
|
// if list1 == nil {
|
||||||
} else {
|
// list1 = list.New()
|
||||||
delete(taskData, scope)
|
// taskList[scope] = list1
|
||||||
}
|
// }
|
||||||
}
|
// if fromHead {
|
||||||
return nil
|
// list1.PushFront(value)
|
||||||
}
|
// } else {
|
||||||
|
// list1.PushBack(value)
|
||||||
|
// }
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
|
||||||
func ListPush(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
// func ListPop(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||||
args := gojs.MakeArgs(&argsIn, vm).Check(2)
|
// args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||||
scope := args.Str(0)
|
// scope := args.Str(0)
|
||||||
value := args.Any(1)
|
// fromEnd := args.Bool(1)
|
||||||
fromHead := args.Bool(2)
|
// taskListLock.Lock()
|
||||||
taskListLock.Lock()
|
// var item *list.Element
|
||||||
defer taskListLock.Unlock()
|
// defer taskListLock.Unlock()
|
||||||
list1 := taskList[scope]
|
// list1 := taskList[scope]
|
||||||
if list1 == nil {
|
// if list1 != nil {
|
||||||
list1 = list.New()
|
// if fromEnd {
|
||||||
taskList[scope] = list1
|
// item = list1.Front()
|
||||||
}
|
// } else {
|
||||||
if fromHead {
|
// item = list1.Back()
|
||||||
list1.PushFront(value)
|
// }
|
||||||
} else {
|
// if item != nil {
|
||||||
list1.PushBack(value)
|
// list1.Remove(item)
|
||||||
}
|
// return vm.ToValue(item.Value)
|
||||||
return nil
|
// }
|
||||||
}
|
// }
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
|
||||||
func ListPop(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
// func ListCount(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||||
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
// args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||||
scope := args.Str(0)
|
// scope := args.Str(0)
|
||||||
fromEnd := args.Bool(1)
|
// taskListLock.RLock()
|
||||||
taskListLock.Lock()
|
// defer taskListLock.RUnlock()
|
||||||
var item *list.Element
|
// if taskList[scope] != nil {
|
||||||
defer taskListLock.Unlock()
|
// return vm.ToValue(taskList[scope].Len())
|
||||||
list1 := taskList[scope]
|
// }
|
||||||
if list1 != nil {
|
// return vm.ToValue(0)
|
||||||
if fromEnd {
|
// }
|
||||||
item = list1.Front()
|
|
||||||
} else {
|
|
||||||
item = list1.Back()
|
|
||||||
}
|
|
||||||
if item != nil {
|
|
||||||
list1.Remove(item)
|
|
||||||
return vm.ToValue(item.Value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ListCount(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
// func ListRemove(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||||
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
// args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||||
scope := args.Str(0)
|
// scope := args.Str(0)
|
||||||
taskListLock.RLock()
|
// taskListLock.Lock()
|
||||||
defer taskListLock.RUnlock()
|
// defer taskListLock.Unlock()
|
||||||
if taskList[scope] != nil {
|
// delete(taskList, scope)
|
||||||
return vm.ToValue(taskList[scope].Len())
|
// return nil
|
||||||
}
|
// }
|
||||||
return vm.ToValue(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ListRemove(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
|
||||||
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
|
||||||
scope := args.Str(0)
|
|
||||||
taskListLock.Lock()
|
|
||||||
defer taskListLock.Unlock()
|
|
||||||
delete(taskList, scope)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,13 +1,12 @@
|
|||||||
import service from "apigo.cc/gojs/service"
|
import service from "apigo.cc/gojs/service"
|
||||||
import co from "apigo.cc/gojs/console"
|
import rt from "apigo.cc/gojs/runtime"
|
||||||
import u from "apigo.cc/gojs/util"
|
|
||||||
|
|
||||||
function main() {
|
function main() {
|
||||||
service.register({ path: '/echo2', noLog200: true }, ({ args, response }) => {
|
service.register({ path: '/echo2', noLog200: true }, ({ args, response }) => {
|
||||||
// setTimeout(() => {
|
// setTimeout(() => {
|
||||||
// response.end(args.name)
|
// response.end(args.name)
|
||||||
// }, 10)
|
// }, 10)
|
||||||
u.sleep(10)
|
rt.sleep(10)
|
||||||
return args.name
|
return args.name
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import s from "apigo.cc/gojs/service"
|
import s from "apigo.cc/gojs/service"
|
||||||
import co from "apigo.cc/gojs/console"
|
import co from "apigo.cc/gojs/console"
|
||||||
|
import task from "apigo.cc/gojs/task"
|
||||||
|
|
||||||
function main() {
|
function main() {
|
||||||
s.register({
|
s.register({
|
||||||
@ -9,11 +10,13 @@ function main() {
|
|||||||
},
|
},
|
||||||
onClose: ({ client }) => {
|
onClose: ({ client }) => {
|
||||||
co.info('ws closed', client.id)
|
co.info('ws closed', client.id)
|
||||||
s.dataRemove('wsTest', client.id)
|
// s.dataRemove('wsTest', client.id)
|
||||||
|
task.remove('wsTest_' + client.id)
|
||||||
}
|
}
|
||||||
}, ({ client }) => {
|
}, ({ client }) => {
|
||||||
co.info('ws connected', client.id)
|
co.info('ws connected', client.id)
|
||||||
s.dataSet('wsTest', client.id, client)
|
// s.dataSet('wsTest', client.id, client)
|
||||||
|
task.set('wsTest_' + client.id, client)
|
||||||
client.write('Hello, World!')
|
client.write('Hello, World!')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,12 +2,14 @@ package service_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"apigo.cc/gojs"
|
"apigo.cc/gojs"
|
||||||
_ "apigo.cc/gojs/console"
|
_ "apigo.cc/gojs/console"
|
||||||
_ "apigo.cc/gojs/http"
|
_ "apigo.cc/gojs/http"
|
||||||
|
_ "apigo.cc/gojs/runtime"
|
||||||
"apigo.cc/gojs/service"
|
"apigo.cc/gojs/service"
|
||||||
_ "apigo.cc/gojs/service"
|
_ "apigo.cc/gojs/service"
|
||||||
_ "apigo.cc/gojs/util"
|
_ "apigo.cc/gojs/util"
|
||||||
@ -38,6 +40,7 @@ func TestStartByPool(t *testing.T) {
|
|||||||
|
|
||||||
gojs.ExportForDev()
|
gojs.ExportForDev()
|
||||||
rt2 = gojs.New()
|
rt2 = gojs.New()
|
||||||
|
u.CopyFile("api/echo.js", "api/echo_tmp.js")
|
||||||
err := rt2.StartFromFile("start.js")
|
err := rt2.StartFromFile("start.js")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("start failed", err)
|
t.Fatal("start failed", err)
|
||||||
@ -216,7 +219,7 @@ func TestStopByPool(t *testing.T) {
|
|||||||
t.Fatal("stop failed", err)
|
t.Fatal("stop failed", err)
|
||||||
}
|
}
|
||||||
gojs.WaitAll()
|
gojs.WaitAll()
|
||||||
|
os.Remove("api/echo_tmp.js")
|
||||||
runtime.GC()
|
runtime.GC()
|
||||||
ms3 := runtime.MemStats{}
|
ms3 := runtime.MemStats{}
|
||||||
runtime.ReadMemStats(&ms3)
|
runtime.ReadMemStats(&ms3)
|
||||||
|
|||||||
@ -2,12 +2,16 @@ package service_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"apigo.cc/gojs"
|
"apigo.cc/gojs"
|
||||||
_ "apigo.cc/gojs/console"
|
_ "apigo.cc/gojs/console"
|
||||||
|
_ "apigo.cc/gojs/file"
|
||||||
_ "apigo.cc/gojs/http"
|
_ "apigo.cc/gojs/http"
|
||||||
|
_ "apigo.cc/gojs/runtime"
|
||||||
_ "apigo.cc/gojs/service"
|
_ "apigo.cc/gojs/service"
|
||||||
_ "apigo.cc/gojs/util"
|
_ "apigo.cc/gojs/util"
|
||||||
"github.com/ssgo/httpclient"
|
"github.com/ssgo/httpclient"
|
||||||
@ -22,6 +26,7 @@ const runTimes = 100
|
|||||||
func TestStart(t *testing.T) {
|
func TestStart(t *testing.T) {
|
||||||
gojs.ExportForDev()
|
gojs.ExportForDev()
|
||||||
rt = gojs.New()
|
rt = gojs.New()
|
||||||
|
u.CopyFile("api/echo.js", "api/echo_tmp.js")
|
||||||
err := rt.StartFromFile("start.js")
|
err := rt.StartFromFile("start.js")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("start failed", err)
|
t.Fatal("start failed", err)
|
||||||
@ -55,6 +60,19 @@ func TestJsEcho(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestJsEcho2(t *testing.T) {
|
||||||
|
for i := 0; i < runTimes; i++ {
|
||||||
|
name := u.UniqueId()
|
||||||
|
r, err := rt.RunCode("test2('" + name + "')")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("test2 js get failed, got error", err)
|
||||||
|
} else if r != name {
|
||||||
|
t.Fatal("test2 js get failed, name not match", r, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
u.WriteFile("api/echo_tmp.js", strings.Replace(u.ReadFileN("api/echo.js"), "return args.name", "return args.name+'!!'", 1))
|
||||||
|
}
|
||||||
|
|
||||||
func TestGoEcho(t *testing.T) {
|
func TestGoEcho(t *testing.T) {
|
||||||
hc := httpclient.GetClientH2C(0)
|
hc := httpclient.GetClientH2C(0)
|
||||||
for i := 0; i < runTimes; i++ {
|
for i := 0; i < runTimes; i++ {
|
||||||
@ -118,6 +136,19 @@ func TestGoAsyncEcho(t *testing.T) {
|
|||||||
fmt.Println(u.BGreen("last name:"), lastName, lastResult)
|
fmt.Println(u.BGreen("last name:"), lastName, lastResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestJsEcho2WithHotLoad(t *testing.T) {
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
for i := 0; i < runTimes; i++ {
|
||||||
|
name := u.UniqueId()
|
||||||
|
r, err := rt.RunCode("test2('" + name + "')")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("test2 js get failed, got error", err)
|
||||||
|
} else if r != name+"!!" {
|
||||||
|
t.Fatal("test2 js get failed, name not match", r, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestStop(t *testing.T) {
|
func TestStop(t *testing.T) {
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
@ -125,6 +156,7 @@ func TestStop(t *testing.T) {
|
|||||||
}()
|
}()
|
||||||
gojs.WaitAll()
|
gojs.WaitAll()
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
os.Remove("api/echo_tmp.js")
|
||||||
// if err != nil {
|
// if err != nil {
|
||||||
// t.Fatal("stop failed", err)
|
// t.Fatal("stop failed", err)
|
||||||
// }
|
// }
|
||||||
|
|||||||
@ -6,7 +6,9 @@ import (
|
|||||||
"apigo.cc/gojs"
|
"apigo.cc/gojs"
|
||||||
_ "apigo.cc/gojs/console"
|
_ "apigo.cc/gojs/console"
|
||||||
_ "apigo.cc/gojs/http"
|
_ "apigo.cc/gojs/http"
|
||||||
|
_ "apigo.cc/gojs/runtime"
|
||||||
_ "apigo.cc/gojs/service"
|
_ "apigo.cc/gojs/service"
|
||||||
|
_ "apigo.cc/gojs/task"
|
||||||
_ "apigo.cc/gojs/util"
|
_ "apigo.cc/gojs/util"
|
||||||
"github.com/ssgo/u"
|
"github.com/ssgo/u"
|
||||||
|
|
||||||
@ -43,7 +45,9 @@ func TestWS(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestStopWSByPool(t *testing.T) {
|
func TestStopWSByPool(t *testing.T) {
|
||||||
_, err := rt3.RunCode("s.stop()")
|
fmt.Println(u.BGreen("stop ws"))
|
||||||
|
_, 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)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
import s from "apigo.cc/gojs/service"
|
import s from "apigo.cc/gojs/service"
|
||||||
import http from "apigo.cc/gojs/http"
|
import http from "apigo.cc/gojs/http"
|
||||||
import u from "apigo.cc/gojs/util"
|
import rt from "apigo.cc/gojs/runtime"
|
||||||
import co from "apigo.cc/gojs/console"
|
import co from "apigo.cc/gojs/console"
|
||||||
|
import file from "apigo.cc/gojs/file"
|
||||||
|
|
||||||
let h2c = http
|
let h2c = http
|
||||||
let urlPrefix
|
let urlPrefix
|
||||||
@ -33,16 +34,17 @@ function main() {
|
|||||||
},
|
},
|
||||||
rewrite: {
|
rewrite: {
|
||||||
'/echo2.js': '/echo.js'
|
'/echo2.js': '/echo.js'
|
||||||
}
|
},
|
||||||
|
hotLoad: 1,
|
||||||
})
|
})
|
||||||
s.register({ path: '/echo', noLog200: true }, ({ args, response }) => {
|
s.register({ path: '/echo', noLog200: true }, ({ args, response }) => {
|
||||||
// setTimeout(() => {
|
// setTimeout(() => {
|
||||||
// response.end(args.name)
|
// response.end(args.name)
|
||||||
// }, 1)
|
// }, 1)
|
||||||
u.sleep(1)
|
rt.sleep(1)
|
||||||
return args.name
|
return args.name
|
||||||
})
|
})
|
||||||
s.load('api/echo.js', { min: 20, max: 1000, idle: 100 })
|
s.load('api/echo_tmp.js', { min: 20, max: 1000, idle: 100 })
|
||||||
s.load('api/user.js')
|
s.load('api/user.js')
|
||||||
let host = s.start()
|
let host = s.start()
|
||||||
h2c = http.newH2C({
|
h2c = http.newH2C({
|
||||||
@ -89,7 +91,7 @@ function testUser() {
|
|||||||
if (r.statusCode != 429) {
|
if (r.statusCode != 429) {
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
u.sleep(100)
|
rt.sleep(100)
|
||||||
|
|
||||||
// 测试限流器过期后允许的 1 次请求
|
// 测试限流器过期后允许的 1 次请求
|
||||||
r = h2c.get('/userInfo').object()
|
r = h2c.get('/userInfo').object()
|
||||||
|
|||||||
@ -1,13 +1,16 @@
|
|||||||
import s from "apigo.cc/gojs/service"
|
import s from "apigo.cc/gojs/service"
|
||||||
import http from "apigo.cc/gojs/http"
|
import http from "apigo.cc/gojs/http"
|
||||||
import u from "apigo.cc/gojs/util"
|
import rt from "apigo.cc/gojs/runtime"
|
||||||
import co from "apigo.cc/gojs/console"
|
import co from "apigo.cc/gojs/console"
|
||||||
|
import task from "apigo.cc/gojs/task"
|
||||||
|
|
||||||
let hc = http
|
let hc = http
|
||||||
let urlPrefix
|
let urlPrefix
|
||||||
function main() {
|
function main() {
|
||||||
s.load('api/ws.js')
|
s.load('api/ws.js')
|
||||||
s.task('task.js', 100)
|
// s.task('task.js', 100)
|
||||||
|
task.addTask("@every 1s", 'task.js')
|
||||||
|
task.start()
|
||||||
let host = s.start()
|
let host = s.start()
|
||||||
hc = http.new({ baseURL: 'http://' + host })
|
hc = http.new({ baseURL: 'http://' + host })
|
||||||
return host
|
return host
|
||||||
@ -31,7 +34,7 @@ function testWS() {
|
|||||||
co.info('test ws abc ok')
|
co.info('test ws abc ok')
|
||||||
|
|
||||||
// ws.ping()
|
// ws.ping()
|
||||||
u.sleep(10)
|
rt.sleep(10)
|
||||||
|
|
||||||
let pc = ws.pingCount()
|
let pc = ws.pingCount()
|
||||||
co.info('test ws ping ok', pc.pingTimes, pc.pongTimes)
|
co.info('test ws ping ok', pc.pingTimes, pc.pongTimes)
|
||||||
@ -51,15 +54,13 @@ function testWS() {
|
|||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
co.info('test ws json ok')
|
co.info('test ws json ok')
|
||||||
|
rt.sleep(2000)
|
||||||
u.sleep(1000)
|
|
||||||
for (let i = 0; i < 5; i++) {
|
for (let i = 0; i < 5; i++) {
|
||||||
let j = ws.read()
|
let j = ws.read()
|
||||||
if (i !== j.data) {
|
if (i !== j.data) {
|
||||||
return j
|
return j
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ws.close()
|
ws.close()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,30 +1,31 @@
|
|||||||
import s from 'apigo.cc/gojs/service'
|
import s from 'apigo.cc/gojs/service'
|
||||||
import co from 'apigo.cc/gojs/console'
|
import co from 'apigo.cc/gojs/console'
|
||||||
|
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 connCount = s.dataCount('wsTest')
|
let keys = task.keys('wsTest_')
|
||||||
if (connCount > 0) {
|
// let connCount = s.dataCount('wsTest')
|
||||||
let conns = s.dataFetch('wsTest')
|
if (keys.length > 0) {
|
||||||
for (let id in conns) {
|
// let conns = s.dataFetch('wsTest')
|
||||||
let conn = conns[id]
|
let conns = task.getAll('wsTest_')
|
||||||
try {
|
for (let id in conns) {
|
||||||
conn.write(i++)
|
let conn = conns[id]
|
||||||
} catch (e) {
|
if (!conn.write(i++)) {
|
||||||
co.error(e)
|
task.remove(id)
|
||||||
s.dataRemove('wsTest', id)
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
co.info('task run', keys.length)
|
||||||
co.info('task run', connCount)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function onStop() {
|
function onStop() {
|
||||||
s.dataRemove('wsTest')
|
// s.dataRemove('wsTest')
|
||||||
co.info('task stop', s.dataCount('wsTest'))
|
task.removeAll('wsTest_')
|
||||||
|
co.info('task stop')
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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' })
|
|
||||||
}
|
}
|
||||||
@ -1,3 +1,3 @@
|
|||||||
<header>
|
<header>
|
||||||
<h1>Welcome to {{bb .title}}</h1>
|
<h1>Welcome to {{bb .title}}</h1>
|
||||||
</header>
|
</header>
|
||||||
@ -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
33
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 {
|
"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)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user