[add] Yao Stuido Server & CLI
This commit is contained in:
parent
cd28cca8d6
commit
26d3f8d8a5
13 changed files with 240 additions and 15 deletions
|
|
@ -1 +0,0 @@
|
|||
package cmd
|
||||
18
cmd/root.go
18
cmd/root.go
|
|
@ -7,6 +7,7 @@ import (
|
|||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/yaoapp/kun/exception"
|
||||
"github.com/yaoapp/yao/cmd/studio"
|
||||
"github.com/yaoapp/yao/config"
|
||||
"github.com/yaoapp/yao/share"
|
||||
)
|
||||
|
|
@ -84,8 +85,23 @@ var rootCmd = &cobra.Command{
|
|||
},
|
||||
}
|
||||
|
||||
var studioCmd = &cobra.Command{
|
||||
Use: "studio",
|
||||
Short: "Yao Studio CLI",
|
||||
Long: `Yao Studio CLI`,
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
CompletionOptions: cobra.CompletionOptions{
|
||||
DisableDefaultCmd: true,
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Fprintln(os.Stderr, L("One or more arguments are not correct"), args)
|
||||
os.Exit(1)
|
||||
},
|
||||
}
|
||||
|
||||
// 加载命令
|
||||
func init() {
|
||||
studioCmd.AddCommand(studio.RunCmd)
|
||||
rootCmd.AddCommand(
|
||||
versionCmd,
|
||||
migrateCmd,
|
||||
|
|
@ -93,11 +109,11 @@ func init() {
|
|||
startCmd,
|
||||
runCmd,
|
||||
initCmd,
|
||||
serviceCmd,
|
||||
dumpCmd,
|
||||
restoreCmd,
|
||||
socketCmd,
|
||||
websocketCmd,
|
||||
studioCmd,
|
||||
)
|
||||
// rootCmd.SetHelpCommand(helpCmd)
|
||||
rootCmd.PersistentFlags().StringVarP(&appPath, "app", "a", "", L("Application directory"))
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/yaoapp/yao/share"
|
||||
)
|
||||
|
||||
// **** DEPRECATED ****
|
||||
var serviceCmd = &cobra.Command{
|
||||
Use: "service",
|
||||
Short: L("Service manager"),
|
||||
|
|
|
|||
31
cmd/start.go
31
cmd/start.go
|
|
@ -23,6 +23,7 @@ import (
|
|||
"github.com/yaoapp/yao/fs"
|
||||
"github.com/yaoapp/yao/service"
|
||||
"github.com/yaoapp/yao/share"
|
||||
"github.com/yaoapp/yao/studio"
|
||||
)
|
||||
|
||||
var startDebug = false
|
||||
|
|
@ -90,11 +91,18 @@ var startCmd = &cobra.Command{
|
|||
|
||||
// development mode
|
||||
if mode == "development" {
|
||||
|
||||
// Start Studio Server
|
||||
go studio.Start(config.Conf)
|
||||
defer studio.Stop()
|
||||
|
||||
printApis(false)
|
||||
printTasks(false)
|
||||
printSchedules(false)
|
||||
printConnectors(false)
|
||||
printStores(false)
|
||||
printStudio(false, host)
|
||||
|
||||
}
|
||||
|
||||
if mode == "development" && !startDisableWatching {
|
||||
|
|
@ -185,6 +193,29 @@ func printStores(silent bool) {
|
|||
}
|
||||
}
|
||||
|
||||
func printStudio(silent bool, host string) {
|
||||
|
||||
if silent {
|
||||
log.Info("[Studio] http://%s:%d", host, config.Conf.Studio.Port)
|
||||
if config.Conf.Studio.Auto {
|
||||
log.Info("[Studio] Secret: %s", config.Conf.Studio.Secret)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(color.WhiteString("\n---------------------------------"))
|
||||
fmt.Println(color.WhiteString(L("Yao Studio Server")))
|
||||
fmt.Println(color.WhiteString("---------------------------------"))
|
||||
fmt.Printf(color.CyanString("HOST : "))
|
||||
fmt.Printf(color.WhiteString(" %s\n", config.Conf.Host))
|
||||
fmt.Printf(color.CyanString("PORT : "))
|
||||
fmt.Printf(color.WhiteString(" %d\n", config.Conf.Studio.Port))
|
||||
if config.Conf.Studio.Auto {
|
||||
fmt.Printf(color.CyanString("SECRET: "))
|
||||
fmt.Printf(color.WhiteString(" %s\n", config.Conf.Studio.Secret))
|
||||
}
|
||||
}
|
||||
|
||||
func printSchedules(silent bool) {
|
||||
|
||||
if len(gou.Schedules) == 0 {
|
||||
|
|
|
|||
93
cmd/studio/run.go
Normal file
93
cmd/studio/run.go
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
package studio
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/yaoapp/gou"
|
||||
"github.com/yaoapp/kun/exception"
|
||||
"github.com/yaoapp/kun/utils"
|
||||
"github.com/yaoapp/yao/config"
|
||||
"github.com/yaoapp/yao/engine"
|
||||
"github.com/yaoapp/yao/share"
|
||||
)
|
||||
|
||||
// RunCmd command
|
||||
var RunCmd = &cobra.Command{
|
||||
Use: "run",
|
||||
Short: L("Execute Yao Studio Script"),
|
||||
Long: L("Execute Yao Studio Script"),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
defer share.SessionStop()
|
||||
defer gou.KillPlugins()
|
||||
defer func() {
|
||||
err := exception.Catch(recover())
|
||||
if err != nil {
|
||||
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
|
||||
}
|
||||
}()
|
||||
|
||||
Boot()
|
||||
cfg := config.Conf
|
||||
cfg.Session.IsCLI = true
|
||||
engine.Load(cfg)
|
||||
if len(args) < 1 {
|
||||
fmt.Println(color.RedString(L("Not enough arguments")))
|
||||
fmt.Println(color.WhiteString(share.BUILDNAME + " help"))
|
||||
return
|
||||
}
|
||||
|
||||
name := strings.Split(args[0], ".")
|
||||
service := strings.Join(name[0:len(name)-1], ".")
|
||||
method := name[len(name)-1]
|
||||
|
||||
fmt.Println(color.GreenString(L("Studio Run: %s"), args[0]))
|
||||
pargs := []interface{}{}
|
||||
for i, arg := range args {
|
||||
if i == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(arg, "::") {
|
||||
arg := strings.TrimPrefix(arg, "::")
|
||||
var v interface{}
|
||||
err := jsoniter.Unmarshal([]byte(arg), &v)
|
||||
if err != nil {
|
||||
fmt.Println(color.RedString(L("Arguments: %s"), err.Error()))
|
||||
return
|
||||
}
|
||||
pargs = append(pargs, v)
|
||||
fmt.Println(color.WhiteString("args[%d]: %s", i-1, arg))
|
||||
} else if strings.HasPrefix(arg, "\\::") {
|
||||
arg := "::" + strings.TrimPrefix(arg, "\\::")
|
||||
pargs = append(pargs, arg)
|
||||
fmt.Println(color.WhiteString("args[%d]: %s", i-1, arg))
|
||||
} else {
|
||||
pargs = append(pargs, arg)
|
||||
fmt.Println(color.WhiteString("args[%d]: %s", i-1, arg))
|
||||
}
|
||||
}
|
||||
|
||||
req := gou.Yao.New(service, method)
|
||||
res, err := req.RootCall(pargs...)
|
||||
if err != nil {
|
||||
fmt.Println(color.RedString("--------------------------------------"))
|
||||
fmt.Println(color.RedString(L("%s Error"), args[0]))
|
||||
fmt.Println(color.RedString("--------------------------------------"))
|
||||
utils.Dump(err)
|
||||
fmt.Println(color.RedString("--------------------------------------"))
|
||||
fmt.Println(color.GreenString(L("✨DONE✨")))
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(color.WhiteString("--------------------------------------"))
|
||||
fmt.Println(color.WhiteString(L("%s Response"), args[0]))
|
||||
fmt.Println(color.WhiteString("--------------------------------------"))
|
||||
utils.Dump(res)
|
||||
fmt.Println(color.WhiteString("--------------------------------------"))
|
||||
fmt.Println(color.GreenString(L("✨DONE✨")))
|
||||
},
|
||||
}
|
||||
53
cmd/studio/utils.go
Normal file
53
cmd/studio/utils.go
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
package studio
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/yaoapp/kun/exception"
|
||||
"github.com/yaoapp/yao/config"
|
||||
)
|
||||
|
||||
var appPath string
|
||||
var envFile string
|
||||
|
||||
var langs = map[string]string{
|
||||
"Start Engine": "启动象传应用引擎",
|
||||
}
|
||||
|
||||
// L 多语言切换
|
||||
func L(words string) string {
|
||||
|
||||
var lang = os.Getenv("YAO_LANG")
|
||||
if lang == "" {
|
||||
return words
|
||||
}
|
||||
|
||||
if trans, has := langs[words]; has {
|
||||
return trans
|
||||
}
|
||||
return words
|
||||
}
|
||||
|
||||
// Boot 设定配置
|
||||
func Boot() {
|
||||
root := config.Conf.Root
|
||||
if appPath != "" {
|
||||
r, err := filepath.Abs(appPath)
|
||||
if err != nil {
|
||||
exception.New("Root error %s", 500, err.Error()).Throw()
|
||||
}
|
||||
root = r
|
||||
}
|
||||
if envFile != "" {
|
||||
config.Conf = config.LoadFrom(envFile)
|
||||
} else {
|
||||
config.Conf = config.LoadFrom(filepath.Join(root, ".env"))
|
||||
}
|
||||
|
||||
if config.Conf.Mode == "production" {
|
||||
config.Production()
|
||||
} else if config.Conf.Mode == "development" {
|
||||
config.Development()
|
||||
}
|
||||
}
|
||||
|
|
@ -27,6 +27,7 @@ import (
|
|||
"github.com/yaoapp/yao/share"
|
||||
"github.com/yaoapp/yao/socket"
|
||||
"github.com/yaoapp/yao/store"
|
||||
"github.com/yaoapp/yao/studio"
|
||||
"github.com/yaoapp/yao/table"
|
||||
"github.com/yaoapp/yao/task"
|
||||
"github.com/yaoapp/yao/websocket"
|
||||
|
|
@ -64,6 +65,12 @@ func Load(cfg config.Config) (err error) {
|
|||
printErr(cfg.Mode, "FileSystem", err)
|
||||
}
|
||||
|
||||
// Load Studio
|
||||
err = studio.Load(cfg)
|
||||
if err != nil {
|
||||
printErr(cfg.Mode, "Studio", err)
|
||||
}
|
||||
|
||||
// 第二步: 建立数据库 & 会话连接
|
||||
share.DBConnect(cfg.DB) // 创建数据库连接
|
||||
// share.SessionConnect(cfg.Session) // 创建会话服务器链接
|
||||
|
|
|
|||
6
fs/fs.go
6
fs/fs.go
|
|
@ -34,9 +34,9 @@ func Load(cfg config.Config) error {
|
|||
}
|
||||
|
||||
fs.Register("system", system.New(dataRoot))
|
||||
fs.Register("dsl", dsl.New(root).DenyAbs(dslDenyList...)) // DSL
|
||||
fs.Register("script", system.New(scriptRoot)) // Script
|
||||
fs.Register("binary", system.New(root)) // Binary
|
||||
// fs.Register("binary", system.New(root)) // Next
|
||||
fs.RootRegister("dsl", dsl.New(root).DenyAbs(dslDenyList...)) // DSL
|
||||
fs.RootRegister("script", system.New(scriptRoot)) // Script
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ func TestLoad(t *testing.T) {
|
|||
|
||||
func TestDSL(t *testing.T) {
|
||||
Load(config.Conf)
|
||||
dsl := fs.MustGet("dsl")
|
||||
dsl := fs.MustRootGet("dsl")
|
||||
name := filepath.Join("models", "test.mod.json")
|
||||
_, err := fs.WriteFile(dsl, name, []byte(`{"foo": "bar", "hello":{ "int": 1, "float": 0.618}}`), 0644)
|
||||
assert.Nil(t, err)
|
||||
|
|
@ -45,11 +45,16 @@ func TestDSL(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = fs.Get("dsl")
|
||||
assert.NotNil(t, err)
|
||||
assert.Contains(t, err.Error(), "dsl does not registered")
|
||||
|
||||
}
|
||||
|
||||
func TestScirpt(t *testing.T) {
|
||||
Load(config.Conf)
|
||||
script := fs.MustGet("script")
|
||||
script := fs.MustRootGet("script")
|
||||
name := "test.js"
|
||||
_, err := fs.WriteFile(script, name, []byte(`console.log("hello")`), 0644)
|
||||
assert.Nil(t, err)
|
||||
|
|
@ -61,4 +66,8 @@ func TestScirpt(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = fs.Get("script")
|
||||
assert.NotNil(t, err)
|
||||
assert.Contains(t, err.Error(), "script does not registered")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,8 +45,18 @@ func hdRecovered(c *gin.Context, recovered interface{}) {
|
|||
c.AbortWithStatus(code)
|
||||
}
|
||||
|
||||
// cross domian
|
||||
func hdCrossDomain(c *gin.Context) {
|
||||
// CORS Cross-origin
|
||||
func hdCORS(c *gin.Context) {
|
||||
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
|
||||
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT")
|
||||
|
||||
if c.Request.Method == "OPTIONS" {
|
||||
c.AbortWithStatus(204)
|
||||
return
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
|
||||
// studio API Auth
|
||||
|
|
@ -63,6 +73,7 @@ func hdAuth(c *gin.Context) {
|
|||
|
||||
claims := helper.JwtValidate(tokenString, config.Conf.Studio.Secret)
|
||||
c.Set("__sid", claims.SID)
|
||||
c.Next()
|
||||
return
|
||||
|
||||
} else if strings.HasPrefix(tokenString, "Signature ") { // For Yao Studio
|
||||
|
|
@ -71,6 +82,7 @@ func hdAuth(c *gin.Context) {
|
|||
ts := c.Request.Header.Get("Studio-Timestamp")
|
||||
query := c.Request.URL.Query()
|
||||
log.Trace("[Studio] %s, %s %s %v", signature, nonce, ts, query)
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ var regExcp = regexp.MustCompile("^Exception\\|([0-9]+):(.+)$")
|
|||
// Serve start the api server
|
||||
func setRouter(router *gin.Engine) {
|
||||
|
||||
router.Use(gin.CustomRecovery(hdRecovered), hdAuth)
|
||||
router.Use(gin.CustomRecovery(hdRecovered), hdCORS, hdAuth)
|
||||
|
||||
// DSL ReadDir, ReadFile
|
||||
router.GET("/dsl/:method", func(c *gin.Context) {
|
||||
|
|
@ -182,7 +182,7 @@ func setRouter(router *gin.Engine) {
|
|||
return
|
||||
}
|
||||
|
||||
service := fmt.Sprintf("__yao.studio.%s", c.Param("name"))
|
||||
service := c.Param("name")
|
||||
|
||||
payload, err := io.ReadAll(c.Request.Body)
|
||||
if err != nil {
|
||||
|
|
@ -207,7 +207,7 @@ func setRouter(router *gin.Engine) {
|
|||
req.WithSid(fmt.Sprintf("%s", sid))
|
||||
}
|
||||
|
||||
res, err := req.Call(fun.Args...)
|
||||
res, err := req.RootCall(fun.Args...)
|
||||
if err != nil {
|
||||
// parse Exception
|
||||
code := 500
|
||||
|
|
|
|||
|
|
@ -134,8 +134,8 @@ func loadScriptFrom(dir string) error {
|
|||
|
||||
messages := []string{}
|
||||
err := share.Walk(dir, ".js", func(root, filename string) {
|
||||
name := fmt.Sprintf("__yao.studio.%s", share.SpecName(root, filename))
|
||||
err := gou.Yao.Load(filename, name)
|
||||
name := share.SpecName(root, filename)
|
||||
err := gou.Yao.RootLoad(filename, name)
|
||||
if err != nil {
|
||||
messages = append(messages, err.Error())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,9 +25,13 @@ func TestLoad(t *testing.T) {
|
|||
}
|
||||
assert.NotNil(t, dfs)
|
||||
|
||||
res, err := gou.Yao.Engine.Call(map[string]interface{}{}, "__yao.studio.table", "Ping")
|
||||
res, err := gou.Yao.Engine.RootCall(map[string]interface{}{}, "table", "Ping")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "PONG", res)
|
||||
|
||||
_, err = gou.Yao.Engine.Call(map[string]interface{}{}, "table", "Ping")
|
||||
assert.NotNil(t, err)
|
||||
assert.Contains(t, err.Error(), "The table does not loaded")
|
||||
}
|
||||
|
||||
func TestStartStop(t *testing.T) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue