diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index b0daf898..b18df465 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -7,36 +7,50 @@ on: branches: [main] env: + YAO_DEV: ${{ github.WORKSPACE }} + YAO_ENV: development + YAO_ROOT: ${{ github.WORKSPACE }}/tests + YAO_HOST: 0.0.0.0 + YAO_PORT: 5099 + YAO_SESSION: "memory" + YAO_LOG: "./logs/application.log" + YAO_LOG_MODE: "TEXT" + YAO_JWT_SECRET: "bLp@bi!oqo-2U+hoTRUG" + YAO_DB_DRIVER: mysql + YAO_DB_PRIMARY: "root:123456@tcp(db-server:3308)/xiang?charset=utf8mb4&parseTime=True&loc=Local" + YAO_DB_SECONDARY: "root:123456@tcp(db-server:3308)/xiang?charset=utf8mb4&parseTime=True&loc=Local" + YAO_DB_AESKEY: "ZLX=T&f6refeCh-ro*r@" + # 象传服务配置 - XIANG_MODE: debug - XIANG_SOURCE: ${{ github.WORKSPACE }} - XIANG_ROOT: ${{ github.WORKSPACE }}/tests - XIANG_ROOT_PLUGIN: ${{ github.WORKSPACE }}/../../../data/gou-unit/plugins + # XIANG_MODE: debug + # XIANG_SOURCE: ${{ github.WORKSPACE }} + # XIANG_ROOT: ${{ github.WORKSPACE }}/tests + # XIANG_ROOT_PLUGIN: ${{ github.WORKSPACE }}/../../../data/gou-unit/plugins # 服务配置 - XIANG_SERVICE_DEBUG: true - XIANG_SERVICE_ALLOW: "xiang.yao.run:3000|xiang.yao.run:3001" - XIANG_SERVICE_HOST: 0.0.0.0 - XIANG_SERVICE_PORT: 5099 + # XIANG_SERVICE_DEBUG: true + # XIANG_SERVICE_ALLOW: "xiang.yao.run:3000|xiang.yao.run:3001" + # XIANG_SERVICE_HOST: 0.0.0.0 + # XIANG_SERVICE_PORT: 5099 # 数据库配置 - XIANG_DB_DEBUG: true - XIANG_DB_DRIVER: "mysql" - XIANG_DB_AESKEY: "ZLX:XzT&f6refeCh-ro*r@" + # XIANG_DB_DEBUG: true + # XIANG_DB_DRIVER: "mysql" + # XIANG_DB_AESKEY: "ZLX:XzT&f6refeCh-ro*r@" # JWT配置 - XIANG_JWT_DEBUG: true - XIANG_JWT_SECRET: "bLp@bi!oqo-2U+hoTRUG" + # XIANG_JWT_DEBUG: true + # XIANG_JWT_SECRET: "bLp@bi!oqo-2U+hoTRUG" # 存储配置 - XIANG_STOR_DEBUG: true - XIANG_STOR_PATH: "fs://${{ github.WORKSPACE }}/upload" + # XIANG_STOR_DEBUG: true + # XIANG_STOR_PATH: "fs://${{ github.WORKSPACE }}/upload" # 日志配置 - XIANG_LOG_ACCESS: "fs://${{ github.WORKSPACE }}/logs/access.log" - XIANG_LOG_ERROR: "fs://${{ github.WORKSPACE }}/logs/error.log" - XIANG_LOG_DB: "fs://${{ github.WORKSPACE }}/logs/db.log" - XIANG_LOG_PLUGIN: "fs://${{ github.WORKSPACE }}/logs/plugin.log" + # XIANG_LOG_ACCESS: "fs://${{ github.WORKSPACE }}/logs/access.log" + # XIANG_LOG_ERROR: "fs://${{ github.WORKSPACE }}/logs/error.log" + # XIANG_LOG_DB: "fs://${{ github.WORKSPACE }}/logs/db.log" + # XIANG_LOG_PLUGIN: "fs://${{ github.WORKSPACE }}/logs/plugin.log" jobs: unit-test: diff --git a/.gitignore b/.gitignore index 71c19d7b..042ca184 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ ui tests/data/*/* debug.log yao-test +dev.sh diff --git a/api/api.go b/api/api.go index 48af0afa..47c48885 100644 --- a/api/api.go +++ b/api/api.go @@ -1,26 +1,40 @@ package api import ( + "fmt" + "path/filepath" + "github.com/yaoapp/gou" + "github.com/yaoapp/kun/log" "github.com/yaoapp/xiang/config" "github.com/yaoapp/xiang/share" ) // Load 加载API -func Load(cfg config.Config) { - LoadFrom(cfg.RootAPI, "") +func Load(cfg config.Config) error { + if share.BUILDIN { + return LoadBuildIn("apis", "") + } + return LoadFrom(filepath.Join(cfg.Root, "apis"), "") } // LoadFrom 从特定目录加载 -func LoadFrom(dir string, prefix string) { - +func LoadFrom(dir string, prefix string) error { if share.DirNotExists(dir) { - return + return fmt.Errorf("%s does not exists", dir) } - - share.Walk(dir, ".http.json", func(root, filename string) { + err := share.Walk(dir, ".http.json", func(root, filename string) { name := prefix + share.SpecName(root, filename) content := share.ReadFile(filename) - gou.LoadAPI(string(content), name) + _, err := gou.LoadAPIReturn(string(content), name) + if err != nil { + log.With(log.F{"root": root, "file": filename}).Error(err.Error()) + } }) + return err +} + +// LoadBuildIn 从制品中读取 +func LoadBuildIn(dir string, prefix string) error { + return nil } diff --git a/app/app.go b/app/app.go index e5f1da7b..15bcedb6 100644 --- a/app/app.go +++ b/app/app.go @@ -1,10 +1,6 @@ package app import ( - "io/ioutil" - "os" - "path/filepath" - jsoniter "github.com/json-iterator/go" "github.com/yaoapp/kun/exception" "github.com/yaoapp/kun/maps" @@ -12,7 +8,6 @@ import ( "github.com/yaoapp/xiang/data" "github.com/yaoapp/xiang/share" "github.com/yaoapp/xiang/xfs" - "github.com/yaoapp/xiang/xlog" ) // Load 加载应用信息 @@ -24,44 +19,44 @@ func Load(cfg config.Config) { // Init 应用初始化 func Init(cfg config.Config) { - // UI文件目录 - if _, err := os.Stat(cfg.RootUI); os.IsNotExist(err) { - err := os.MkdirAll(cfg.RootUI, os.ModePerm) - if err != nil { - xlog.Printf("创建目录失败(%s) %s", cfg.RootUI, err) - os.Exit(1) - } + // // UI文件目录 + // if _, err := os.Stat(cfg.RootUI); os.IsNotExist(err) { + // err := os.MkdirAll(cfg.RootUI, os.ModePerm) + // if err != nil { + // xlog.Printf("创建目录失败(%s) %s", cfg.RootUI, err) + // os.Exit(1) + // } - content, err := data.Asset("xiang/data/index.html") - if err != nil { - xlog.Printf("读取文件失败(%s) %s", cfg.RootUI, err) - os.Exit(1) - } + // content, err := data.Asset("xiang/data/index.html") + // if err != nil { + // xlog.Printf("读取文件失败(%s) %s", cfg.RootUI, err) + // os.Exit(1) + // } - err = ioutil.WriteFile(filepath.Join(cfg.RootUI, "/index.html"), content, os.ModePerm) - if err != nil { - xlog.Printf("复制默认文件失败(%s) %s", cfg.RootUI, err) - os.Exit(1) - } - } + // err = ioutil.WriteFile(filepath.Join(cfg.RootUI, "/index.html"), content, os.ModePerm) + // if err != nil { + // xlog.Printf("复制默认文件失败(%s) %s", cfg.RootUI, err) + // os.Exit(1) + // } + // } - // 数据库目录 - if _, err := os.Stat(cfg.RootDB); os.IsNotExist(err) { - err := os.MkdirAll(cfg.RootDB, os.ModePerm) - if err != nil { - xlog.Printf("创建目录失败(%s) %s", cfg.RootDB, err) - os.Exit(1) - } - } + // // 数据库目录 + // if _, err := os.Stat(cfg.RootDB); os.IsNotExist(err) { + // err := os.MkdirAll(cfg.RootDB, os.ModePerm) + // if err != nil { + // xlog.Printf("创建目录失败(%s) %s", cfg.RootDB, err) + // os.Exit(1) + // } + // } - // 文件数据目录 - if _, err := os.Stat(cfg.RootData); os.IsNotExist(err) { - err := os.MkdirAll(cfg.RootData, os.ModePerm) - if err != nil { - xlog.Printf("创建目录失败(%s) %s", cfg.RootData, err) - os.Exit(1) - } - } + // // 文件数据目录 + // if _, err := os.Stat(cfg.RootData); os.IsNotExist(err) { + // err := os.MkdirAll(cfg.RootData, os.ModePerm) + // if err != nil { + // xlog.Printf("创建目录失败(%s) %s", cfg.RootData, err) + // os.Exit(1) + // } + // } } // LoadInfo 应用信息 diff --git a/chart/chart.go b/chart/chart.go index 15cc87ee..12f838a8 100644 --- a/chart/chart.go +++ b/chart/chart.go @@ -2,41 +2,54 @@ package chart import ( "fmt" + "path/filepath" jsoniter "github.com/json-iterator/go" "github.com/yaoapp/gou" "github.com/yaoapp/kun/exception" + "github.com/yaoapp/kun/log" "github.com/yaoapp/xiang/config" "github.com/yaoapp/xiang/share" - "github.com/yaoapp/xiang/xlog" ) // Charts 已载入图表 var Charts = map[string]*Chart{} // Load 加载数据表格 -func Load(cfg config.Config) { - LoadFrom(cfg.RootChart, "") +func Load(cfg config.Config) error { + if share.BUILDIN { + return LoadBuildIn("charts", "") + } + return LoadFrom(filepath.Join(cfg.Root, "charts"), "") +} + +// LoadBuildIn 从制品中读取 +func LoadBuildIn(dir string, prefix string) error { + return nil } // LoadFrom 从特定目录加载 -func LoadFrom(dir string, prefix string) { +func LoadFrom(dir string, prefix string) error { if share.DirNotExists(dir) { - return + return fmt.Errorf("%s does not exists", dir) } - share.Walk(dir, ".json", func(root, filename string) { + err := share.Walk(dir, ".json", func(root, filename string) { name := prefix + share.SpecName(root, filename) content := share.ReadFile(filename) _, err := LoadChart(content, name) if err != nil { - exception.New("%s 图表格式错误", 400, name).Ctx(filename).Throw() + log.With(log.F{"root": root, "file": filename}).Error(err.Error()) } }) + if err != nil { + return err + } + // Load Script - share.Walk(dir, ".js", func(root, filename string) { + err = share.Walk(dir, ".js", func(root, filename string) { name := prefix + share.SpecName(root, filename) chart := Select(name) if chart != nil { @@ -45,6 +58,8 @@ func LoadFrom(dir string, prefix string) { chart.LoadScript(string(content), script) } }) + + return err } // LoadChart 载入数据表格 @@ -56,9 +71,7 @@ func LoadChart(source []byte, name string) (*Chart, error) { } err := jsoniter.Unmarshal(source, chart) if err != nil { - xlog.Println(name) - xlog.Println(err.Error()) - xlog.Println(string(source)) + log.With(log.F{"name": name, "source": source}).Error(err.Error()) return nil, err } chart.Prepare() diff --git a/chart/chart_test.go b/chart/chart_test.go index e1344720..02f8fac0 100644 --- a/chart/chart_test.go +++ b/chart/chart_test.go @@ -11,7 +11,7 @@ import ( ) func TestLoad(t *testing.T) { - share.DBConnect(config.Conf.Database) + share.DBConnect(config.Conf.DB) model.Load(config.Conf) query.Load(config.Conf) diff --git a/chart/process_test.go b/chart/process_test.go index 7d0a744b..a5f83921 100644 --- a/chart/process_test.go +++ b/chart/process_test.go @@ -17,7 +17,7 @@ import ( ) func init() { - share.DBConnect(config.Conf.Database) + share.DBConnect(config.Conf.DB) model.Load(config.Conf) query.Load(config.Conf) Load(config.Conf) diff --git a/cmd/domain.go b/cmd/domain.go deleted file mode 100644 index 4795930a..00000000 --- a/cmd/domain.go +++ /dev/null @@ -1,18 +0,0 @@ -package cmd - -import ( - "fmt" - - "github.com/spf13/cobra" - "github.com/yaoapp/xiang/share" -) - -var domainCmd = &cobra.Command{ - Use: "domain", - Short: "显示绑定域名", - Long: `显示绑定域名`, - Run: func(cmd *cobra.Command, args []string) { - // Do Stuff Here - fmt.Println(share.DOMAIN) - }, -} diff --git a/cmd/inspect.go b/cmd/inspect.go index a1251db5..315edaf1 100644 --- a/cmd/inspect.go +++ b/cmd/inspect.go @@ -16,7 +16,6 @@ var inspectCmd = &cobra.Command{ Boot() res := maps.Map{ "version": share.VERSION, - "domain": share.DOMAIN, "config": config.Conf, } utils.Dump(res) diff --git a/cmd/root.go b/cmd/root.go index 35c33946..17cde175 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -30,11 +30,9 @@ var rootCmd = &cobra.Command{ func init() { rootCmd.AddCommand( versionCmd, - // domainCmd, migrateCmd, inspectCmd, startCmd, - // importCmd, runCmd, ) rootCmd.SetHelpCommand(helpCmd) @@ -54,15 +52,18 @@ func Execute() { func Boot() { if envFile == "" && appPath != "" { - config.SetAppPath(appPath, filepath.Join(appPath, ".env")) + config.Conf = config.LoadFrom(filepath.Join(appPath, ".env")) + config.Conf.Root = appPath return } - if envFile != "" { // 指定环境变量文件 - config.SetEnvFile(envFile) + if envFile != "" && appPath == "" { // 指定环境变量文件 + // config.SetEnvFile(envFile) + config.Conf = config.LoadFrom(envFile) } - if appPath != "" { - config.SetAppPath(appPath) + if envFile != "" && appPath != "" { + config.Conf = config.LoadFrom(envFile) + config.Conf.Root = appPath } } diff --git a/cmd/run.go b/cmd/run.go index 5e934725..c34d04ce 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -21,7 +21,7 @@ var runCmd = &cobra.Command{ defer gou.KillPlugins() Boot() cfg := config.Conf - cfg.Session.IsCLI = true + // cfg.Session.IsCLI = true engine.Load(cfg) if len(args) < 1 { fmt.Println(color.RedString("参数错误: 未指定处理名称")) diff --git a/cmd/start.go b/cmd/start.go index 64137298..c215d801 100644 --- a/cmd/start.go +++ b/cmd/start.go @@ -36,7 +36,6 @@ var startCmd = &cobra.Command{ fmt.Printf(color.WhiteString("\n---------------------------------")) fmt.Printf(color.GreenString("\n应用名称: %s v%s", share.App.Name, share.App.Version)) fmt.Printf(color.GreenString("\n应用根目录: %s", config.Conf.Root)) - fmt.Printf(color.GreenString("\n数据存储目录: %s", config.Conf.RootData)) fmt.Printf(color.GreenString("\n数据存储引擎: %s", share.App.Storage.Default)) fmt.Printf(color.WhiteString("\n---------------------------------\n\n")) @@ -58,12 +57,7 @@ var startCmd = &cobra.Command{ } - domain := share.DOMAIN - if domain == "*.iqka.com" { - domain = "local.iqka.com" - } - - port := fmt.Sprintf(":%d", config.Conf.Service.Port) + port := fmt.Sprintf(":%d", config.Conf.Port) if port == ":80" { port = "" } @@ -73,7 +67,7 @@ var startCmd = &cobra.Command{ fmt.Printf(color.GreenString("\n前台界面: http://%s%s/\n", "127.0.0.1", port)) fmt.Printf(color.GreenString("管理后台: http://%s%s/xiang/login/admin\n", "127.0.0.1", port)) fmt.Printf(color.GreenString("API 接口: http://%s%s/api\n", "127.0.0.1", port)) - fmt.Printf(color.GreenString("跨域访问: %s\n\n", strings.Join(config.Conf.Service.Allow, ","))) + // fmt.Printf(color.GreenString("跨域访问: %s\n\n", strings.Join(config.Conf.Service.Allow, ","))) // 调试模式 if config.Conf.Mode == "debug" { diff --git a/config/config.go b/config/config.go index 1a039080..90e5616a 100644 --- a/config/config.go +++ b/config/config.go @@ -1,64 +1,110 @@ package config import ( - "io" - "log" + "errors" "os" "path/filepath" - "strings" + + "github.com/yaoapp/kun/log" "github.com/caarlos0/env/v6" "github.com/joho/godotenv" - "github.com/yaoapp/gou/helper" "github.com/yaoapp/kun/exception" ) // Conf 配置参数 var Conf Config -// Config 系统配置 -type Config struct { - XiangConfig - Service ServiceConfig `json:"service,omitempty"` - Session SessionConfig `json:"session,omitempty"` - Database DatabaseConfig `json:"database,omitempty"` - JWT JWTConfig `json:"jwt,omitempty"` - Log LogConfig `json:"log,omitempty"` +func init() { + filename, _ := filepath.Abs(filepath.Join(".", ".env")) + if _, err := os.Stat(filename); errors.Is(err, os.ErrNotExist) { + Conf = Load() + return + } + Conf = LoadFrom(filename) } -// XiangConfig 象传应用引擎配置 -type XiangConfig struct { - Mode string `json:"mode,omitempty" env:"XIANG_MODE" envDefault:"release"` // 象传引擎模式 debug/release/test - Source string `json:"source,omitempty" env:"XIANG_SOURCE" envDefault:"fs://."` // 源码路径(用于单元测试载入数据) - Path string `json:"path,omitempty" env:"XIANG_PATH" envDefault:"bin://xiang"` // 引擎文件目录 - Root string `json:"root,omitempty" env:"XIANG_ROOT" envDefault:"fs://."` // 应用文件目录 - RootUI string `json:"root_ui,omitempty" env:"XIANG_ROOT_UI"` // 应用界面静态文件目录 - RootDB string `json:"root_db,omitempty" env:"XIANG_ROOT_DB"` // 应用SQLite数据库目录 - RootData string `json:"root_data,omitempty" env:"XIANG_ROOT_DATA"` // 应用数据文件目录 - RootAPI string `json:"root_api,omitempty" env:"XIANG_ROOT_API"` // 应用API文件目录 - RootModel string `json:"root_model,omitempty" env:"XIANG_ROOT_MODEL"` // 应用模型文件目录 - RootFLow string `json:"root_flow,omitempty" env:"XIANG_ROOT_FLOW"` // 应用业务逻辑文件目录 - RootPlugin string `json:"root_plugin,omitempty" env:"XIANG_ROOT_PLUGIN"` // 应用业务插件文件目录 - RootLib string `json:"root_lib,omitempty" env:"XIANG_ROOT_LIB"` // 应用资料库文件目录 - RootTable string `json:"root_table,omitempty" env:"XIANG_ROOT_TABLE"` // 应用数据表格文件目录 - RootChart string `json:"root_chart,omitempty" env:"XIANG_ROOT_CHART"` // 应用分析图表文件目录 - RootPage string `json:"root_page,omitempty" env:"XIANG_ROOT_PAGE"` // 应用通用页面文件目录 - RootScreen string `json:"root_screen,omitempty" env:"XIANG_ROOT_SCREEN"` // 应用大屏文件目录 - RootForm string `json:"root_form,omitempty" env:"XIANG_ROOT_FORM"` // 表单文件目录 - RootWorkFlow string `json:"root_workflow,omitempty" env:"XIANG_ROOT_WORKFLOW"` // 工作流文件目录 +// LoadFrom 从配置项中加载 +func LoadFrom(envfile string) Config { + + file, err := filepath.Abs(envfile) + if err != nil { + log.Warn("Can't load env file. %s", err.Error()) + } + err = godotenv.Overload(file) + if err != nil { + log.Warn("Can't load env file. %s", err.Error()) + } + + return Load() } -// ServiceConfig 服务配置 -type ServiceConfig struct { - Debug bool `json:"debug,omitempty" env:"XIANG_SERVICE_DEBUG" envDefault:"false"` // DEBUG 开关 - HTTPS bool `json:"https,omitempty" env:"XIANG_SERVICE_HTTPS" envDefault:"false"` // HTTPS 开关 - Cert string `json:"cert,omitempty" env:"XIANG_SERVICE_CERT"` // HTTPS 证书 - Key string `json:"key,omitempty" env:"XIANG_SERVICE_KEY"` // HTTPS 证书密钥 - Allow []string `json:"allow,omitempty" env:"XIANG_SERVICE_ALLOW" envSeparator:"|"` // 跨域访问域名列表 - Host string `json:"host,omitempty" env:"XIANG_SERVICE_HOST" envDefault:"127.0.0.1"` // 服务监听IP - Port int `json:"port,omitempty" env:"XIANG_SERVICE_PORT" envDefault:"5099"` // 服务监听端口 +// Load 加载配置 +func Load() Config { + cfg := Config{} + if err := env.Parse(&cfg); err != nil { + exception.New("Can't read config %s", 500, err.Error()).Throw() + } + cfg.Root, _ = filepath.Abs(cfg.Root) + return cfg } +// // Config 系统配置 +// type Config struct { +// XiangConfig +// Service ServiceConfig `json:"service,omitempty"` +// Session SessionConfig `json:"session,omitempty"` +// Database DatabaseConfig `json:"database,omitempty"` +// JWT JWTConfig `json:"jwt,omitempty"` +// Log LogConfig `json:"log,omitempty"` +// } + +// // JWTConfig JWT配置 +// type JWTConfig struct { +// Secret string `json:"secret,omitempty" env:"YAO_JWT_SECRET"` // JWT 密钥 +// } + +// // LogConfig 日志配置 +// type LogConfig struct { +// Access string `json:"access,omitempty" env:"XIANG_LOG_ACCESS" envDefault:"os://stdout"` // 服务访问日志 +// Error string `json:"error,omitempty" env:"XIANG_LOG_ERROR" envDefault:"os://stderr"` // 服务错误日志 +// DB string `json:"database,omitempty" env:"XIANG_LOG_DB" envDefault:"os://stdout"` // 数据库日志 +// Plugin string `json:"plugin,omitempty" env:"XIANG_LOG_PLUGIN" envDefault:"os://stdout"` // 插件日志 +// } + +// // XiangConfig 象传应用引擎配置 +// type XiangConfig struct { +// Mode string `json:"mode,omitempty" env:"XIANG_MODE" envDefault:"release"` // 象传引擎模式 debug/release/test +// Source string `json:"source,omitempty" env:"XIANG_SOURCE" envDefault:"fs://."` // 源码路径(用于单元测试载入数据) +// Path string `json:"path,omitempty" env:"XIANG_PATH" envDefault:"bin://xiang"` // 引擎文件目录 +// Root string `json:"root,omitempty" env:"XIANG_ROOT" envDefault:"fs://."` // 应用文件目录 +// RootUI string `json:"root_ui,omitempty" env:"XIANG_ROOT_UI"` // 应用界面静态文件目录 +// RootDB string `json:"root_db,omitempty" env:"XIANG_ROOT_DB"` // 应用SQLite数据库目录 +// RootData string `json:"root_data,omitempty" env:"XIANG_ROOT_DATA"` // 应用数据文件目录 +// RootAPI string `json:"root_api,omitempty" env:"XIANG_ROOT_API"` // 应用API文件目录 +// RootModel string `json:"root_model,omitempty" env:"XIANG_ROOT_MODEL"` // 应用模型文件目录 +// RootFLow string `json:"root_flow,omitempty" env:"XIANG_ROOT_FLOW"` // 应用业务逻辑文件目录 +// RootPlugin string `json:"root_plugin,omitempty" env:"XIANG_ROOT_PLUGIN"` // 应用业务插件文件目录 +// RootLib string `json:"root_lib,omitempty" env:"XIANG_ROOT_LIB"` // 应用资料库文件目录 +// RootTable string `json:"root_table,omitempty" env:"XIANG_ROOT_TABLE"` // 应用数据表格文件目录 +// RootChart string `json:"root_chart,omitempty" env:"XIANG_ROOT_CHART"` // 应用分析图表文件目录 +// RootPage string `json:"root_page,omitempty" env:"XIANG_ROOT_PAGE"` // 应用通用页面文件目录 +// RootScreen string `json:"root_screen,omitempty" env:"XIANG_ROOT_SCREEN"` // 应用大屏文件目录 +// RootForm string `json:"root_form,omitempty" env:"XIANG_ROOT_FORM"` // 表单文件目录 +// RootWorkFlow string `json:"root_workflow,omitempty" env:"XIANG_ROOT_WORKFLOW"` // 工作流文件目录 +// } + +// // ServiceConfig 服务配置 +// type ServiceConfig struct { +// Debug bool `json:"debug,omitempty" env:"XIANG_SERVICE_DEBUG" envDefault:"false"` // DEBUG 开关 +// HTTPS bool `json:"https,omitempty" env:"XIANG_SERVICE_HTTPS" envDefault:"false"` // HTTPS 开关 +// Cert string `json:"cert,omitempty" env:"XIANG_SERVICE_CERT"` // HTTPS 证书 +// Key string `json:"key,omitempty" env:"XIANG_SERVICE_KEY"` // HTTPS 证书密钥 +// Allow []string `json:"allow,omitempty" env:"XIANG_SERVICE_ALLOW" envSeparator:"|"` // 跨域访问域名列表 +// Host string `json:"host,omitempty" env:"XIANG_SERVICE_HOST" envDefault:"127.0.0.1"` // 服务监听IP +// Port int `json:"port,omitempty" env:"XIANG_SERVICE_PORT" envDefault:"5099"` // 服务监听端口 +// } + // SessionConfig 会话服务器 type SessionConfig struct { Debug bool `json:"debug,omitempty" env:"XIANG_SESSION_DEBUG" envDefault:"false"` // DEBUG 开关 @@ -68,204 +114,186 @@ type SessionConfig struct { Port int `json:"port,omitempty" env:"XIANG_SESSION_PORT" envDefault:"3322"` // 会话服务器端口 } -// DatabaseConfig 数据库配置 -type DatabaseConfig struct { - Debug bool `json:"debug,omitempty" env:"XIANG_DB_DEBUG" envDefault:"false"` // DEBUG 开关 - Driver string `json:"driver,omitempty" env:"XIANG_DB_DRIVER" envDefault:"sqlite3"` // 数据库驱动 ( sqlite3, mysql, postgres) - Primary []string `json:"primary,omitempty" env:"XIANG_DB_PRIMARY" envSeparator:"|" envDefault:"file:./db/xiang.db"` // 主库连接DSN - Secondary []string `json:"secondary,omitempty" env:"XIANG_DB_SECONDARY" envSeparator:"|"` // 从库连接DSN - AESKey string `json:"aeskey,omitempty" env:"XIANG_DB_AESKEY"` // 加密存储KEY -} +// // DatabaseConfig 数据库配置 +// type DatabaseConfig struct { +// Debug bool `json:"debug,omitempty" env:"XIANG_DB_DEBUG" envDefault:"false"` // DEBUG 开关 +// Driver string `json:"driver,omitempty" env:"XIANG_DB_DRIVER" envDefault:"sqlite3"` // 数据库驱动 ( sqlite3, mysql, postgres) +// Primary []string `json:"primary,omitempty" env:"XIANG_DB_PRIMARY" envSeparator:"|" envDefault:"file:./db/xiang.db"` // 主库连接DSN +// Secondary []string `json:"secondary,omitempty" env:"XIANG_DB_SECONDARY" envSeparator:"|"` // 从库连接DSN +// AESKey string `json:"aeskey,omitempty" env:"XIANG_DB_AESKEY"` // 加密存储KEY +// } -// JWTConfig JWT配置 -type JWTConfig struct { - Debug bool `json:"debug,omitempty" env:"XIANG_JWT_DEBUG" envDefault:"false"` // DEBUG 开关 - Secret string `json:"secret,omitempty" env:"XIANG_JWT_SECRET"` // JWT 密钥 -} +// // NewConfig 创建配置文件 +// func NewConfig(envfile ...string) Config { -// LogConfig 日志配置 -type LogConfig struct { - Access string `json:"access,omitempty" env:"XIANG_LOG_ACCESS" envDefault:"os://stdout"` // 服务访问日志 - Error string `json:"error,omitempty" env:"XIANG_LOG_ERROR" envDefault:"os://stderr"` // 服务错误日志 - DB string `json:"database,omitempty" env:"XIANG_LOG_DB" envDefault:"os://stdout"` // 数据库日志 - Plugin string `json:"plugin,omitempty" env:"XIANG_LOG_PLUGIN" envDefault:"os://stdout"` // 插件日志 -} +// filename := os.Getenv("XIANG_ENV_FILE") +// if filename == "" { +// filename = ".env" +// } -// NewConfig 创建配置文件 -func NewConfig(envfile ...string) Config { +// if len(envfile) > 0 { +// file, err := filepath.Abs(envfile[0]) +// if err == nil { +// // log.Printf("加载环境配置文件%s出错 %s\n", envfile[0], err.Error()) +// // } else { +// filename = file +// } +// } - filename := os.Getenv("XIANG_ENV_FILE") - if filename == "" { - filename = ".env" - } +// godotenv.Overload(filename) +// // if err != nil { +// // log.Printf("加载环境配置文件%s出错 %s\n", filename, err.Error()) +// // } - if len(envfile) > 0 { - file, err := filepath.Abs(envfile[0]) - if err == nil { - // log.Printf("加载环境配置文件%s出错 %s\n", envfile[0], err.Error()) - // } else { - filename = file - } - } +// cfg := Config{} +// if err := env.Parse(&cfg); err != nil { +// exception.New("解析配置文件出错 %s", 500, err.Error()).Throw() +// } - godotenv.Overload(filename) - // if err != nil { - // log.Printf("加载环境配置文件%s出错 %s\n", filename, err.Error()) - // } +// cfg.SetDefaults() +// return cfg +// } - cfg := Config{} - if err := env.Parse(&cfg); err != nil { - exception.New("解析配置文件出错 %s", 500, err.Error()).Throw() - } +// // NewConfigFrom 创建配置文件 +// func NewConfigFrom(input io.Reader) Config { +// cfg := Config{} +// err := helper.UnmarshalFile(input, &cfg) +// if err != nil { +// exception.New("解析配置文件出错 %s", 500, err.Error()).Throw() +// } +// cfg.SetDefaults() +// return cfg +// } - cfg.SetDefaults() - return cfg -} +// // SetDefaults 设定默认值 +// func (cfg *Config) SetDefaults() { -// NewConfigFrom 创建配置文件 -func NewConfigFrom(input io.Reader) Config { - cfg := Config{} - err := helper.UnmarshalFile(input, &cfg) - if err != nil { - exception.New("解析配置文件出错 %s", 500, err.Error()).Throw() - } - cfg.SetDefaults() - return cfg -} +// pwd, err := os.Getwd() +// if err != nil { +// log.Fatal(err.Error()) +// } -// SetDefaults 设定默认值 -func (cfg *Config) SetDefaults() { +// if cfg.Root == "fs://." || cfg.Root == "." { +// cfg.Root = pwd +// } - pwd, err := os.Getwd() - if err != nil { - log.Fatal(err) - } +// if cfg.RootAPI == "" { +// cfg.RootAPI = cfg.Root + "/apis" +// } +// if cfg.RootFLow == "" { +// cfg.RootFLow = cfg.Root + "/flows" +// } +// if cfg.RootModel == "" { +// cfg.RootModel = cfg.Root + "/models" +// } +// if cfg.RootPlugin == "" { +// cfg.RootPlugin = cfg.Root + "/plugins" +// } - if cfg.Root == "fs://." || cfg.Root == "." { - cfg.Root = pwd - } +// if cfg.RootTable == "" { +// cfg.RootTable = cfg.Root + "/tables" +// } - if cfg.RootAPI == "" { - cfg.RootAPI = cfg.Root + "/apis" - } - if cfg.RootFLow == "" { - cfg.RootFLow = cfg.Root + "/flows" - } - if cfg.RootModel == "" { - cfg.RootModel = cfg.Root + "/models" - } - if cfg.RootPlugin == "" { - cfg.RootPlugin = cfg.Root + "/plugins" - } +// if cfg.RootLib == "" { +// cfg.RootLib = cfg.Root + "/libs" +// } - if cfg.RootTable == "" { - cfg.RootTable = cfg.Root + "/tables" - } +// if cfg.RootChart == "" { +// cfg.RootChart = cfg.Root + "/charts" +// } - if cfg.RootLib == "" { - cfg.RootLib = cfg.Root + "/libs" - } +// if cfg.RootPage == "" { +// cfg.RootPage = cfg.Root + "/pages" +// } - if cfg.RootChart == "" { - cfg.RootChart = cfg.Root + "/charts" - } +// if cfg.RootWorkFlow == "" { +// cfg.RootWorkFlow = cfg.Root + "/workflows" +// } - if cfg.RootPage == "" { - cfg.RootPage = cfg.Root + "/pages" - } +// if cfg.RootData == "" { +// cfg.RootData = cfg.Root + "/data" +// } - if cfg.RootWorkFlow == "" { - cfg.RootWorkFlow = cfg.Root + "/workflows" - } +// if cfg.RootDB == "" { +// cfg.RootDB = cfg.Root + "/db" +// } - if cfg.RootData == "" { - cfg.RootData = cfg.Root + "/data" - } +// if cfg.RootUI == "" { +// cfg.RootUI = cfg.Root + "/ui" +// } - if cfg.RootDB == "" { - cfg.RootDB = cfg.Root + "/db" - } +// if len(cfg.Database.Primary) > 0 { +// if strings.HasPrefix(cfg.Database.Primary[0], "file:.") { +// cfg.Database.Primary[0] = strings.ReplaceAll(cfg.Database.Primary[0], "file:.", pwd) +// } else if strings.HasPrefix(cfg.Database.Primary[0], "file:/") { +// cfg.Database.Primary[0] = strings.ReplaceAll(cfg.Database.Primary[0], "file:.", cfg.RootDB) +// } +// } - if cfg.RootUI == "" { - cfg.RootUI = cfg.Root + "/ui" - } +// // 过滤数据 +// cfg.RootDB = strings.TrimPrefix(cfg.RootDB, "fs://") +// cfg.RootDB = strings.TrimPrefix(cfg.RootDB, "file://") +// cfg.RootData = strings.TrimPrefix(cfg.RootData, "fs://") +// cfg.RootData = strings.TrimPrefix(cfg.RootData, "file://") +// cfg.RootUI = strings.TrimPrefix(cfg.RootUI, "fs://") +// cfg.RootUI = strings.TrimPrefix(cfg.RootUI, "file://") +// cfg.RootLib = strings.TrimPrefix(cfg.RootLib, "fs://") +// cfg.RootLib = strings.TrimPrefix(cfg.RootLib, "file://") - if len(cfg.Database.Primary) > 0 { - if strings.HasPrefix(cfg.Database.Primary[0], "file:.") { - cfg.Database.Primary[0] = strings.ReplaceAll(cfg.Database.Primary[0], "file:.", pwd) - } else if strings.HasPrefix(cfg.Database.Primary[0], "file:/") { - cfg.Database.Primary[0] = strings.ReplaceAll(cfg.Database.Primary[0], "file:.", cfg.RootDB) - } - } +// } - // 过滤数据 - cfg.RootDB = strings.TrimPrefix(cfg.RootDB, "fs://") - cfg.RootDB = strings.TrimPrefix(cfg.RootDB, "file://") - cfg.RootData = strings.TrimPrefix(cfg.RootData, "fs://") - cfg.RootData = strings.TrimPrefix(cfg.RootData, "file://") - cfg.RootUI = strings.TrimPrefix(cfg.RootUI, "fs://") - cfg.RootUI = strings.TrimPrefix(cfg.RootUI, "file://") - cfg.RootLib = strings.TrimPrefix(cfg.RootLib, "fs://") - cfg.RootLib = strings.TrimPrefix(cfg.RootLib, "file://") +// // SetEnvFile 指定ENV文件 +// func SetEnvFile(filename string) { +// Conf = NewConfig(filename) +// } -} +// // SetAppPath 设定应用目录 +// func SetAppPath(root string, envfile ...string) { -// SetEnvFile 指定ENV文件 -func SetEnvFile(filename string) { - Conf = NewConfig(filename) -} +// fullpath, err := filepath.Abs(root) +// if err != nil { +// log.Panic("设定应用目录%s出错 %s\n", root, err.Error()) +// } -// SetAppPath 设定应用目录 -func SetAppPath(root string, envfile ...string) { +// // 创建应用目录 +// pathinfo, err := os.Stat(fullpath) +// if os.IsNotExist(err) { +// err := os.MkdirAll(fullpath, os.ModePerm) +// if err != nil { +// log.Panic("创建目录失败(%s) %s", root, err) +// } +// } +// pathinfo, err = os.Stat(fullpath) +// if !pathinfo.IsDir() { +// log.Panic("检查应用目录失败(%s) ", err) +// } - fullpath, err := filepath.Abs(root) - if err != nil { - log.Panicf("设定应用目录%s出错 %s\n", root, err.Error()) - } +// if !pathinfo.IsDir() { +// log.Panic("应用目录不是文件夹(%s) ", root) +// } - // 创建应用目录 - pathinfo, err := os.Stat(fullpath) - if os.IsNotExist(err) { - err := os.MkdirAll(fullpath, os.ModePerm) - if err != nil { - log.Panicf("创建目录失败(%s) %s", root, err) - } - } - pathinfo, err = os.Stat(fullpath) - if !pathinfo.IsDir() { - log.Panicf("检查应用目录失败(%s) ", err) - } +// // Set ENV +// if len(envfile) > 0 { +// Conf = NewConfig(envfile[0]) +// } - if !pathinfo.IsDir() { - log.Panicf("应用目录不是文件夹(%s) ", root) - } +// // 从加载配置文件 +// Conf.Root = fullpath +// Conf.RootAPI = filepath.Join(fullpath, "/apis") +// Conf.RootFLow = filepath.Join(fullpath, "/flows") +// Conf.RootModel = filepath.Join(fullpath, "/models") +// Conf.RootPlugin = filepath.Join(fullpath, "/plugins") +// Conf.RootTable = filepath.Join(fullpath, "/tables") +// Conf.RootChart = filepath.Join(fullpath, "/charts") +// Conf.RootPage = filepath.Join(fullpath, "/pages") +// Conf.RootWorkFlow = filepath.Join(fullpath, "/workflows") +// Conf.RootData = filepath.Join(fullpath, "/data") +// Conf.RootUI = filepath.Join(fullpath, "/ui") +// Conf.RootDB = filepath.Join(fullpath, "/db") +// Conf.RootLib = filepath.Join(fullpath, "/libs") +// } - // Set ENV - if len(envfile) > 0 { - Conf = NewConfig(envfile[0]) - } - - // 从加载配置文件 - Conf.Root = fullpath - Conf.RootAPI = filepath.Join(fullpath, "/apis") - Conf.RootFLow = filepath.Join(fullpath, "/flows") - Conf.RootModel = filepath.Join(fullpath, "/models") - Conf.RootPlugin = filepath.Join(fullpath, "/plugins") - Conf.RootTable = filepath.Join(fullpath, "/tables") - Conf.RootChart = filepath.Join(fullpath, "/charts") - Conf.RootPage = filepath.Join(fullpath, "/pages") - Conf.RootWorkFlow = filepath.Join(fullpath, "/workflows") - Conf.RootData = filepath.Join(fullpath, "/data") - Conf.RootUI = filepath.Join(fullpath, "/ui") - Conf.RootDB = filepath.Join(fullpath, "/db") - Conf.RootLib = filepath.Join(fullpath, "/libs") -} - -// IsDebug 是否为调试模式 -func IsDebug() bool { - return Conf.Mode == "debug" -} - -func init() { - Conf = NewConfig() -} +// // IsDebug 是否为调试模式 +// func IsDebug() bool { +// return Conf.Mode == "debug" +// } diff --git a/config/config_test.go b/config/config_test.go index 22821ea5..ae62e375 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -2,52 +2,65 @@ package config import ( "os" - "strings" + "path/filepath" "testing" "github.com/stretchr/testify/assert" - "github.com/yaoapp/kun/any" ) -func TestNewConfig(t *testing.T) { - cfg := NewConfig() - var vBool = func(name string) bool { - if name == "true" || name == "1" { - return true - } - return false - } +// func TestNewConfig(t *testing.T) { +// cfg := NewConfig() +// var vBool = func(name string) bool { +// if name == "true" || name == "1" { +// return true +// } +// return false +// } - xiangPath := os.Getenv("XIANG_PATH") - if xiangPath == "" { - xiangPath = "bin://xiang" - } +// xiangPath := os.Getenv("XIANG_PATH") +// if xiangPath == "" { +// xiangPath = "bin://xiang" +// } - assert.Equal(t, cfg.Mode, os.Getenv("XIANG_MODE")) - assert.Equal(t, cfg.Root, os.Getenv("XIANG_ROOT")) - assert.Equal(t, cfg.Path, xiangPath) +// assert.Equal(t, cfg.Mode, os.Getenv("XIANG_MODE")) +// assert.Equal(t, cfg.Root, os.Getenv("XIANG_ROOT")) +// assert.Equal(t, cfg.Path, xiangPath) - assert.Equal(t, cfg.Service.Debug, vBool(os.Getenv("XIANG_SERVICE_DEBUG"))) - assert.Equal(t, strings.Join(cfg.Service.Allow, "|"), os.Getenv("XIANG_SERVICE_ALLOW")) - assert.Equal(t, cfg.Service.Host, os.Getenv("XIANG_SERVICE_HOST")) - assert.Equal(t, cfg.Service.Port, any.Of(os.Getenv("XIANG_SERVICE_PORT")).CInt()) +// assert.Equal(t, cfg.Service.Debug, vBool(os.Getenv("XIANG_SERVICE_DEBUG"))) +// assert.Equal(t, strings.Join(cfg.Service.Allow, "|"), os.Getenv("XIANG_SERVICE_ALLOW")) +// assert.Equal(t, cfg.Service.Host, os.Getenv("XIANG_SERVICE_HOST")) +// assert.Equal(t, cfg.Service.Port, any.Of(os.Getenv("XIANG_SERVICE_PORT")).CInt()) - assert.Equal(t, cfg.Database.Debug, vBool(os.Getenv("XIANG_DB_DEBUG"))) - assert.Equal(t, strings.Join(cfg.Database.Primary, "|"), os.Getenv("XIANG_DB_PRIMARY")) - assert.Equal(t, strings.Join(cfg.Database.Secondary, "|"), os.Getenv("XIANG_DB_SECONDARY")) - assert.Equal(t, cfg.Database.AESKey, os.Getenv("XIANG_DB_AESKEY")) +// assert.Equal(t, cfg.Database.Debug, vBool(os.Getenv("XIANG_DB_DEBUG"))) +// assert.Equal(t, strings.Join(cfg.Database.Primary, "|"), os.Getenv("XIANG_DB_PRIMARY")) +// assert.Equal(t, strings.Join(cfg.Database.Secondary, "|"), os.Getenv("XIANG_DB_SECONDARY")) +// assert.Equal(t, cfg.Database.AESKey, os.Getenv("XIANG_DB_AESKEY")) - assert.Equal(t, cfg.JWT.Debug, vBool(os.Getenv("XIANG_JWT_DEBUG"))) - assert.Equal(t, cfg.JWT.Secret, os.Getenv("XIANG_JWT_SECRET")) +// assert.Equal(t, cfg.JWT.Secret, os.Getenv("XIANG_JWT_SECRET")) - assert.Equal(t, cfg.Log.Access, os.Getenv("XIANG_LOG_ACCESS")) - assert.Equal(t, cfg.Log.Error, os.Getenv("XIANG_LOG_ERROR")) - assert.Equal(t, cfg.Log.DB, os.Getenv("XIANG_LOG_DB")) - assert.Equal(t, cfg.Log.Plugin, os.Getenv("XIANG_LOG_PLUGIN")) +// assert.Equal(t, cfg.Log.Access, os.Getenv("XIANG_LOG_ACCESS")) +// assert.Equal(t, cfg.Log.Error, os.Getenv("XIANG_LOG_ERROR")) +// assert.Equal(t, cfg.Log.DB, os.Getenv("XIANG_LOG_DB")) +// assert.Equal(t, cfg.Log.Plugin, os.Getenv("XIANG_LOG_PLUGIN")) -} - -func TestNewConfigFrom(t *testing.T) { - assert.True(t, true) - assert.True(t, true) +// } + +// func TestNewConfigFrom(t *testing.T) { +// assert.True(t, true) +// assert.True(t, true) +// } + +func TestLoadFrom(t *testing.T) { + cfg := LoadFrom(filepath.Join(os.Getenv("YAO_DEV"), ".env")) + root, _ := filepath.Abs(os.Getenv("YAO_ROOT")) + assert.Equal(t, cfg.Root, root) + assert.Equal(t, cfg.Mode, os.Getenv("YAO_ENV")) + assert.Equal(t, cfg.Host, os.Getenv("YAO_HOST")) + assert.Equal(t, cfg.Port, os.Getenv("YAO_PORT")) + assert.Equal(t, cfg.JWTSecret, os.Getenv("YAO_JWT_SECRET")) + assert.Equal(t, cfg.Log, os.Getenv("YAO_LOG")) + assert.Equal(t, cfg.LogMode, os.Getenv("YAO_LOG_MODE")) + assert.Equal(t, cfg.DB.Driver, os.Getenv("YAO_DB_DRIVER")) + assert.Equal(t, cfg.DB.Primary[0], os.Getenv("YAO_DB_PRIMARY")) + assert.Equal(t, cfg.DB.Secondary[0], os.Getenv("YAO_DB_SECONDARY")) } diff --git a/config/types.go b/config/types.go new file mode 100644 index 00000000..3372f4b3 --- /dev/null +++ b/config/types.go @@ -0,0 +1,25 @@ +package config + +// Config 象传应用引擎配置 +type Config struct { + Mode string `json:"mode,omitempty" env:"YAO_ENV" envDefault:"production"` // 象传引擎启动模式 production/development + Root string `json:"root,omitempty" env:"YAO_ROOT" envDefault:"."` // 应用根目录 + Host string `json:"host,omitempty" env:"YAO_HOST" envDefault:"0.0.0.0"` // 服务监听地址 + Port int `json:"port,omitempty" env:"YAO_PORT" envDefault:"5099"` // 服务监听端口 + Cert string `json:"cert,omitempty" env:"YAO_CERT"` // HTTPS 证书文件地址 + Key string `json:"key,omitempty" env:"YAO_KEY"` // HTTPS 证书密钥地址 + Log string `json:"log,omitempty" env:"YAO_LOG" envDefault:"./logs/application.log"` // 服务日志地址 + LogMode string `json:"log_mode,omitempty" env:"YAO_LOG_MODE" envDefault:"JSON"` // 服务日志模式 JSON|TEXT + // Session string `json:"session,omitempty" env:"YAO_SESSION" envDefault:"memory"` // 用户会话模式 memory|redis|database + JWTSecret string `json:"jwt_secret,omitempty" env:"YAO_JWT_SECRET"` // JWT 密钥 + DB DBConfig `json:"db,omitempty"` // 数据库配置 + Session SessionConfig `json:"session,omitempty"` +} + +// DBConfig 数据库配置 +type DBConfig struct { + Driver string `json:"driver,omitempty" env:"YAO_DB_DRIVER" envDefault:"sqlite3"` // 数据库驱动 sqlite3| mysql| postgres + Primary []string `json:"primary,omitempty" env:"YAO_DB_PRIMARY" envSeparator:"|" envDefault:"./db/yao.db"` // 主库连接DSN + Secondary []string `json:"secondary,omitempty" env:"YAO_DB_SECONDARY" envSeparator:"|"` // 从库连接DSN + AESKey string `json:"aeskey,omitempty" env:"YAO_DB_AESKEY"` // 加密存储KEY +} diff --git a/data/bindata.go b/data/bindata.go index 1cf34307..158803e6 100644 --- a/data/bindata.go +++ b/data/bindata.go @@ -1,16 +1,15 @@ // Code generated for package data by go-bindata DO NOT EDIT. (@generated) // sources: // .tmp/data/ui/index.html -// .tmp/data/xiang/.DS_Store // .tmp/data/xiang/apis/README.md // .tmp/data/xiang/apis/chart.http.json +// .tmp/data/xiang/apis/import.http.json // .tmp/data/xiang/apis/page.http.json // .tmp/data/xiang/apis/storage.http.json // .tmp/data/xiang/apis/table.http.json // .tmp/data/xiang/apis/user.http.json // .tmp/data/xiang/apis/xiang.http.json // .tmp/data/xiang/data/app.json -// .tmp/data/xiang/data/icons/.DS_Store // .tmp/data/xiang/data/icons/404.png // .tmp/data/xiang/data/icons/icon.icns // .tmp/data/xiang/data/icons/icon.ico @@ -204,27 +203,7 @@ func uiIndexHtml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "ui/index.html", size: 12, mode: os.FileMode(420), modTime: time.Unix(1641540958, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _xiangDs_store = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x97\x4f\x4b\xe3\x40\x14\xc0\xdf\xa4\xd9\xee\x64\xcb\xb2\x39\x2c\x6c\x8f\xb9\xec\xad\x87\xb6\xdb\x5d\xe8\x61\x21\x64\xbb\x07\x6f\x42\x44\x0f\x55\xda\x0c\x09\x36\x90\x66\x4a\x92\x1a\x34\x46\xfa\x35\xf4\x1b\xf9\x09\xfc\x3a\x92\xe6\xa9\x25\xb6\xde\xc4\x5a\xdf\x0f\xca\x6f\xca\xfc\x49\xde\x84\xf9\xf3\x00\x80\x59\x73\xb7\x03\xa0\x03\x00\x87\xd2\xea\x17\x58\x0b\xc7\xdf\x33\x14\xb4\x5a\x8c\xb7\x1c\x23\x80\x58\xa4\xf1\x4c\x04\x52\xac\x1f\x8b\x20\x88\x2d\xa2\x58\xbb\x75\x98\x82\x04\x17\xbc\xca\xfa\xbd\x15\xb3\xc0\x8f\x93\x76\xfb\x8e\x29\x35\xf5\x53\xfd\x33\xe7\xbc\xc1\xbf\xf2\x13\x7b\x22\x53\x3b\x71\x92\x79\x6c\x39\xd1\xb0\xf8\xb7\xef\x24\x13\x81\xe5\x03\x29\x83\xc7\xb2\x23\x0e\x7d\x2f\x1d\xe9\xdf\xff\xc9\x30\x71\xfc\xd0\x8b\x96\x9d\x7d\xd7\x13\x4e\x74\x7c\xe4\x87\xae\x4c\x2d\x39\x0f\xdd\x78\xb8\x52\xa1\x69\x1a\xd7\x46\x7a\x33\xcb\x7a\xbf\xfe\xb4\x8c\xde\xef\x6e\xde\x32\xb2\x7e\xb7\xdd\x32\x7a\xfd\x6e\x9e\x6b\xfc\xc7\xcf\xce\xdf\xbd\xf1\xf4\xfc\x22\xbb\xcc\xaf\xae\xcb\x58\x18\xc3\xa0\xbe\x55\x82\xbc\xa9\x06\x79\x66\x47\x61\x20\xc3\xd3\x72\xe3\x82\x3a\x24\xe0\x80\x80\x00\xbc\x5d\x9e\x80\x95\x20\x2b\x13\x40\x7c\x44\xf0\xcb\xf3\xc6\x5b\xbf\x08\x41\x10\x5b\x47\xb1\x3f\x18\x68\x13\xbd\x28\xcd\xb0\x5e\x41\xab\x2b\x7d\x74\xb4\x81\x36\xd1\x8b\xd2\x0c\xdb\x29\x68\x15\xcd\xd1\x3a\xda\x40\x9b\xe8\x45\x69\xdc\xb4\x18\x26\x1f\x0c\x9f\xcc\x30\x43\x61\x3a\xda\x40\x9b\xaf\x33\x37\x04\xf1\xde\xa9\x95\xd2\x8b\xf3\xff\xff\xe6\xfc\x9f\x20\x88\x1d\x86\xa9\x03\x7b\x60\xbd\x90\x0a\x2a\x78\x11\x18\x3f\x74\xd8\x70\x11\xc0\xb6\xc5\x51\xdc\x84\xa7\xb6\x74\x11\x20\x88\x2d\xe3\x3e\x00\x00\xff\xff\xc8\x24\xe8\x37\x04\x18\x00\x00") - -func xiangDs_storeBytes() ([]byte, error) { - return bindataRead( - _xiangDs_store, - "xiang/.DS_Store", - ) -} - -func xiangDs_store() (*asset, error) { - bytes, err := xiangDs_storeBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "xiang/.DS_Store", size: 6148, mode: os.FileMode(420), modTime: time.Unix(1641540958, 0)} + info := bindataFileInfo{name: "ui/index.html", size: 12, mode: os.FileMode(420), modTime: time.Unix(1644337051, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -244,7 +223,7 @@ func xiangApisReadmeMd() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "xiang/apis/README.md", size: 13, mode: os.FileMode(420), modTime: time.Unix(1641540958, 0)} + info := bindataFileInfo{name: "xiang/apis/README.md", size: 13, mode: os.FileMode(420), modTime: time.Unix(1644337051, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -264,7 +243,27 @@ func xiangApisChartHttpJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "xiang/apis/chart.http.json", size: 624, mode: os.FileMode(420), modTime: time.Unix(1641540958, 0)} + info := bindataFileInfo{name: "xiang/apis/chart.http.json", size: 624, mode: os.FileMode(420), modTime: time.Unix(1644337051, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _xiangApisImportHttpJson = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xcc\x54\xbb\x4e\xc3\x30\x14\xdd\xf3\x15\x96\xc5\x18\x92\xc0\x98\x0d\x09\x84\x18\x10\x15\x65\xab\x3a\x5c\x1a\x93\xba\x6a\x62\x63\x3b\x40\xa9\x3a\x82\x58\x78\x2c\xac\x2c\xa8\x2b\xf0\x01\xfc\x4e\xf3\x1d\xc8\xb1\xdb\x90\x2a\xa0\xaa\x95\x28\x4b\xa2\x9c\x73\xee\xe3\xdc\x7b\x95\xa1\x83\x10\x4e\x21\x21\x38\x44\x38\x7f\xfe\xc8\xef\xdf\x26\xef\x9f\x93\x9b\x71\xfe\x30\x9e\x3c\xbe\x62\x57\xf3\x17\x44\x48\xca\x52\x2d\xd9\xf2\x02\x2f\x30\x68\x44\x64\x47\x50\xae\x2c\x63\x83\xef\x6e\xf3\x97\x27\x13\xbc\xd3\x38\x30\xca\x58\xb0\x8c\x6b\xcd\x15\x85\x34\xf6\x69\xc2\x99\x50\x96\xca\x40\x44\x9a\x3a\x25\x20\x88\xd8\xec\x5d\x5a\x82\x83\xea\x4a\x1c\xa2\x96\x83\x10\x42\xc3\xe2\x69\x61\x2d\xf7\xc3\xa2\x69\x77\x8a\x27\x44\x75\x59\x91\xa8\x71\xd4\x3c\x29\x71\x2e\x58\x87\x48\x39\x2b\xee\x99\xe2\xde\x71\x96\x96\x22\xaa\x0d\xb4\xf0\x06\x07\x01\x89\x67\x12\x23\xfd\x39\xe8\x33\x88\xbc\x33\xda\xaf\x02\x09\x70\x4e\xd3\x18\xb7\x67\x19\x58\xa6\x70\x38\xeb\x12\x21\x2c\x15\xa8\x4c\x97\xdd\x0e\x02\xb7\x84\xd5\x80\x17\x93\x06\xce\xfb\xb4\x03\x7a\x76\x7e\x4f\xb2\x14\x5b\xc9\xa8\x78\x8f\xdc\xdf\x4c\xfb\x92\x28\xa5\xcb\xd7\x98\xdf\xdf\x5b\xc0\x7b\x73\x3e\xbe\xc6\xff\x9a\xac\x45\xa0\x60\xf9\xa5\xee\x56\xa2\x8d\xab\xb2\xc1\xca\x7a\x2b\xf0\xf7\x35\xd7\x10\x1c\xe2\x7a\x42\xd2\xeb\x7a\x62\x7a\x20\x96\x5a\xe3\x30\x57\x3e\x16\x3d\xd3\x7f\x7c\x30\xd3\x49\x2f\x6b\xef\x70\x3e\xfe\x87\x7f\xc1\x79\x46\xc4\xc0\x9c\xc8\x7a\x9d\xae\xbc\x50\xeb\x78\x91\x9d\xfe\x95\x71\x07\xa1\xb6\x33\x72\xbe\x02\x00\x00\xff\xff\x17\xf7\x73\xea\x8c\x06\x00\x00") + +func xiangApisImportHttpJsonBytes() ([]byte, error) { + return bindataRead( + _xiangApisImportHttpJson, + "xiang/apis/import.http.json", + ) +} + +func xiangApisImportHttpJson() (*asset, error) { + bytes, err := xiangApisImportHttpJsonBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "xiang/apis/import.http.json", size: 1676, mode: os.FileMode(420), modTime: time.Unix(1644337051, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -284,7 +283,7 @@ func xiangApisPageHttpJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "xiang/apis/page.http.json", size: 609, mode: os.FileMode(420), modTime: time.Unix(1641540958, 0)} + info := bindataFileInfo{name: "xiang/apis/page.http.json", size: 609, mode: os.FileMode(420), modTime: time.Unix(1644337051, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -304,12 +303,12 @@ func xiangApisStorageHttpJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "xiang/apis/storage.http.json", size: 1092, mode: os.FileMode(420), modTime: time.Unix(1641540958, 0)} + info := bindataFileInfo{name: "xiang/apis/storage.http.json", size: 1092, mode: os.FileMode(420), modTime: time.Unix(1644337051, 0)} a := &asset{bytes: bytes, info: info} return a, nil } -var _xiangApisTableHttpJson = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xd4\x56\xcd\x6e\xd3\x40\x10\xbe\xfb\x29\x56\xab\x1e\x53\x3b\x70\xf4\x0d\x89\x1f\xe5\x44\x51\x41\x1c\xaa\x1e\x06\xef\x90\x0c\xb5\xd7\xdb\xfd\x49\x31\x55\x1e\x01\x38\x71\x07\x21\x24\x4e\x88\x07\xe0\x75\x88\x78\x0c\xb4\x6b\xa7\xa9\x43\xd2\x86\x58\xc8\xe1\x92\x68\x67\xe6\x1b\xcd\xf7\x7d\x1e\x7b\x2f\x23\xc6\xb8\x84\x02\x79\xca\xf8\xfc\xc3\xf7\xf9\xdb\x6f\xbf\x3e\x7d\x9d\x7f\xfc\x31\x7f\xf7\xe5\xe7\xfb\xcf\x7c\xe0\xf3\x53\xd4\x86\x4a\xe9\x4b\xee\xc4\xc3\x78\x58\x47\x05\x9a\x4c\x93\xb2\x4d\xe6\x4f\xf0\xbd\xa3\x51\x5d\x39\xd6\xa5\x53\xbe\xe6\x35\x81\x1c\x27\x16\x5e\xe4\xd8\x64\x1c\x68\xe1\x33\x24\x0f\x95\x2e\x33\x34\xa6\x4e\x28\xb0\x13\xc3\x53\x76\x12\x31\xc6\xd8\x65\xf8\x6d\xc2\xbe\x3c\x49\xfd\xcc\x89\x41\xd0\xd9\x24\x20\x42\xba\x40\x3b\x29\x43\xbf\x47\x0f\x9e\x2e\xc3\x8b\xce\x8b\x09\xe2\x30\x41\x7c\xbc\x82\x26\xcf\xe3\x84\x1f\x28\xd0\x50\xc4\x41\x94\x01\xe3\xe9\xb9\x43\x5d\x1d\x86\xa0\x3f\x1f\x84\x73\xac\x60\x8c\x2b\x47\x43\x6f\x90\x9f\x5e\xb5\x2b\x9d\xe5\xe9\xd5\xe8\x8c\x71\x63\xc1\x3a\x3f\xc5\xdd\xe1\x70\xb0\x0c\xdb\x4a\x05\xf5\x41\xa9\x9c\x32\xf0\x7a\x26\xaf\x4c\x29\x79\x53\x32\x0b\xff\xb3\xc1\x8d\x4a\xbc\x24\x29\x92\x94\xc4\xae\x5a\x3c\x24\x29\x6e\x53\xa2\x39\x92\xe8\x8b\xa4\xc1\x1c\x33\xbb\xbb\xdd\x6d\x74\x4d\x72\x39\x62\x8b\xed\xb5\x70\x6d\xf0\x19\x56\x17\xa5\x16\x6b\x32\x1b\x00\x53\xc8\xdd\xba\x78\x4e\x05\xd9\x05\xed\xbe\x84\x84\x29\xae\x93\xf1\xe8\xf1\xf1\x16\x3a\xb6\xc0\x1b\x96\x46\x41\x95\x97\xd0\xdb\x93\x72\xee\x28\x3b\xeb\xc4\xf2\x89\xef\xf0\x3f\x50\x15\x98\xa3\xc5\x4d\xbb\xbf\x15\xd7\xfb\xa1\xc5\xde\xaf\x3f\x49\x83\x7a\xed\xfa\x6f\xc5\x72\xb4\x02\xdf\xc8\x32\xd8\x19\x67\x65\xee\x0a\x69\x5a\xb1\xb0\xd3\xa6\x67\xa7\x2f\x26\xa8\x77\x7f\xae\x6b\xaf\x9f\xb7\x7b\x6c\xf1\xe5\xeb\x97\x33\xc9\x8e\x84\x47\xf2\x56\xe3\xeb\xd7\x33\x09\x73\xfd\xb3\xae\xa9\x00\x5d\xf5\xc5\xde\x29\x01\x9d\x1d\x7f\x16\x9a\xfc\xbd\xe3\x7b\xf0\x6e\x6b\xf8\x77\x70\xbf\x26\xdf\xd1\xfd\x3d\x50\xc2\xa0\xb5\x24\xc7\xbb\xdf\x7d\x56\xe0\x37\xaa\xd0\xdc\xb3\xfe\x19\xd5\x88\xb1\xd3\x68\x16\xfd\x0e\x00\x00\xff\xff\xf6\xae\xeb\xee\x7c\x0c\x00\x00") +var _xiangApisTableHttpJson = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xd4\x56\xcd\x6e\xd3\x40\x10\xbe\xe7\x29\x56\xab\x1e\x53\x3b\x70\xf4\x0d\x89\x1f\xe5\x44\x51\x41\x1c\xaa\x1e\xa6\xde\x21\x99\xd6\x5e\x6f\xf7\x27\xc1\x54\x79\x04\xe0\xc4\x1d\x84\x90\x38\x21\x1e\x80\xd7\x21\xe2\x31\xd0\xae\x4d\x52\x87\xa4\x0d\xb1\x90\xd3\x4b\xa2\x9d\x99\x6f\x34\xdf\xf7\x79\xec\xbd\xea\x31\xc6\x25\xe4\xc8\x13\xc6\xe7\x1f\xbe\xcf\xdf\x7e\xfb\xf5\xe9\xeb\xfc\xe3\x8f\xf9\xbb\x2f\x3f\xdf\x7f\xe6\x7d\x9f\x9f\xa0\x36\x54\x48\x5f\x72\x2f\x1a\x44\x83\x2a\x2a\xd0\xa4\x9a\x94\xad\x33\x7f\x83\x1f\x1c\x0d\xab\xca\x91\x2e\x9c\xf2\x35\xaf\x09\xe4\x28\xb6\x70\x96\x61\x9d\x71\xa0\x85\xcf\x9c\x21\x68\xd4\x87\xe7\x53\x5b\x25\x14\xd8\xb1\xe1\x09\x3b\xe9\x31\xc6\xd8\x55\xf8\xad\xc3\xbe\x3c\x4e\xfc\xcc\xb1\x41\xd0\xe9\x38\x20\x42\x3a\x47\x3b\x2e\x42\xbf\x27\x8f\x9e\x2f\xc3\x4a\x17\x29\x1a\xb3\x98\x20\x0a\x13\x44\xc7\x2b\x68\xf2\x3c\x4e\xf8\x81\x02\x0d\x79\x14\x44\xe9\x33\x9e\x5c\x3a\xd4\xe5\x61\x08\xfa\xf3\x41\x38\x47\x0a\x46\xb8\x72\x34\xf4\x06\xf9\xe9\xa2\x5d\xe1\x2c\x4f\x16\xa3\x33\xc6\x8d\x05\xeb\xfc\x14\xf7\x07\x83\xfe\x32\x6c\x4b\x15\xd4\x07\xa5\x32\x4a\xc1\xeb\x19\x9f\x9b\x42\xf2\xba\x64\x16\xfe\x67\xfd\x1b\x95\x78\x45\x52\xc4\x09\x89\x5d\xb5\x78\x4c\x52\xdc\xa6\x44\x7d\x24\xd1\x15\x49\x83\x19\xa6\x76\x77\xbb\x9b\xe8\x8a\xe4\x72\xc4\x06\xdb\x6b\xe1\xca\xe0\x0b\x2c\xa7\x85\x16\x6b\x32\x1b\x00\x13\xc8\xdc\xba\x78\x46\x39\xd9\x3f\xb4\xbb\x12\x12\x26\xb8\x4e\xc6\xa3\xa7\xc7\x5b\xe8\xd8\x00\x6f\x58\x1a\x05\x65\x56\x40\x67\x4f\xca\xa5\xa3\xf4\xa2\x15\xcb\x67\xbe\xc3\x5d\xa0\x2a\x30\x43\x8b\x9b\x76\x7f\x2b\xae\x0f\x43\x8b\xbd\x5f\x7f\x92\x06\xf5\xda\xf5\xdf\x8a\xe5\x70\x05\xbe\x91\x65\xb0\x33\x4a\x8b\xcc\xe5\xd2\x34\x62\x61\xa7\x4d\xc7\x4e\x4f\xc7\xa8\x77\x7f\xae\x2b\xaf\x5f\x36\x7b\x6c\xf1\xe5\xeb\x96\x33\xc9\x96\x84\x87\xf2\x56\xe3\xab\xd7\x33\x09\x73\xfd\xb3\xae\x29\x07\x5d\x76\xc5\xde\x29\x01\xad\x1d\x7f\x11\x9a\xfc\xbb\xe3\x7b\xf0\x6e\xab\xf9\xb7\x70\xbf\x22\xdf\xd2\xfd\x3d\x50\xc2\xa0\xb5\x24\x47\xbb\xdf\x7d\x56\xe0\x37\xaa\x50\xdf\xb3\xfe\x1b\xd5\x1e\x63\xa7\xbd\x59\xef\x77\x00\x00\x00\xff\xff\x8a\x3e\xea\x17\x7c\x0c\x00\x00") func xiangApisTableHttpJsonBytes() ([]byte, error) { return bindataRead( @@ -324,7 +323,7 @@ func xiangApisTableHttpJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "xiang/apis/table.http.json", size: 3196, mode: os.FileMode(420), modTime: time.Unix(1641540958, 0)} + info := bindataFileInfo{name: "xiang/apis/table.http.json", size: 3196, mode: os.FileMode(420), modTime: time.Unix(1644337051, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -344,7 +343,7 @@ func xiangApisUserHttpJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "xiang/apis/user.http.json", size: 1453, mode: os.FileMode(420), modTime: time.Unix(1641540958, 0)} + info := bindataFileInfo{name: "xiang/apis/user.http.json", size: 1453, mode: os.FileMode(420), modTime: time.Unix(1644337051, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -364,7 +363,7 @@ func xiangApisXiangHttpJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "xiang/apis/xiang.http.json", size: 1239, mode: os.FileMode(420), modTime: time.Unix(1641540958, 0)} + info := bindataFileInfo{name: "xiang/apis/xiang.http.json", size: 1239, mode: os.FileMode(420), modTime: time.Unix(1644337051, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -384,27 +383,7 @@ func xiangDataAppJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "xiang/data/app.json", size: 245, mode: os.FileMode(420), modTime: time.Unix(1641540958, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _xiangDataIconsDs_store = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\xd8\x31\x0a\x02\x31\x10\x85\xe1\x37\x31\x45\xc0\x26\xa5\x65\x1a\x0f\xe0\x0d\xc2\xb2\x9e\xc0\x0b\x58\x78\x05\xfb\x1c\x5d\x96\x79\x60\x60\xd5\x4e\x8c\xcb\xfb\x40\xfe\x05\x37\x2a\x16\x31\x23\x00\x9b\xee\xb7\x13\x90\x01\x24\x78\x71\xc4\x4b\x89\x8f\x95\xd0\x5d\x1b\x5f\x43\x44\x44\x44\xc6\x66\x9e\xb4\xff\xf5\x07\x11\x91\xe1\x2c\xfb\x43\x61\x2b\xdb\xbc\xc6\xe7\x03\x1b\xbb\x35\x99\x2d\x6c\x65\x9b\xd7\x78\x5f\x60\x23\x9b\xd8\xcc\x16\xb6\xb2\xcd\xcb\x4d\xcb\x38\x7c\x18\xdf\xd9\x38\xa1\x18\xa7\x10\x2b\x6c\xfd\xce\x77\x23\xf2\xef\x76\x9e\xbc\xfc\xfe\x9f\xdf\xcf\xff\x22\xb2\x61\x16\xe7\xcb\x3c\x3d\x07\x82\xf5\x0d\x00\xae\xdd\xf5\xa7\x43\x40\xf0\x3f\x0b\x0f\xdd\x5a\x1d\x04\x44\x06\xf3\x08\x00\x00\xff\xff\x6a\x00\x88\x6d\x04\x18\x00\x00") - -func xiangDataIconsDs_storeBytes() ([]byte, error) { - return bindataRead( - _xiangDataIconsDs_store, - "xiang/data/icons/.DS_Store", - ) -} - -func xiangDataIconsDs_store() (*asset, error) { - bytes, err := xiangDataIconsDs_storeBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "xiang/data/icons/.DS_Store", size: 6148, mode: os.FileMode(420), modTime: time.Unix(1641540958, 0)} + info := bindataFileInfo{name: "xiang/data/app.json", size: 245, mode: os.FileMode(420), modTime: time.Unix(1644337051, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -424,7 +403,7 @@ func xiangDataIcons404Png() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "xiang/data/icons/404.png", size: 9342, mode: os.FileMode(420), modTime: time.Unix(1641540958, 0)} + info := bindataFileInfo{name: "xiang/data/icons/404.png", size: 9342, mode: os.FileMode(420), modTime: time.Unix(1644337051, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -444,7 +423,7 @@ func xiangDataIconsIconIcns() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "xiang/data/icons/icon.icns", size: 75636, mode: os.FileMode(420), modTime: time.Unix(1641540958, 0)} + info := bindataFileInfo{name: "xiang/data/icons/icon.icns", size: 75636, mode: os.FileMode(420), modTime: time.Unix(1644337051, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -464,7 +443,7 @@ func xiangDataIconsIconIco() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "xiang/data/icons/icon.ico", size: 31310, mode: os.FileMode(420), modTime: time.Unix(1641540958, 0)} + info := bindataFileInfo{name: "xiang/data/icons/icon.ico", size: 31310, mode: os.FileMode(420), modTime: time.Unix(1644337051, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -484,7 +463,7 @@ func xiangDataIconsIconPng() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "xiang/data/icons/icon.png", size: 22420, mode: os.FileMode(420), modTime: time.Unix(1641540958, 0)} + info := bindataFileInfo{name: "xiang/data/icons/icon.png", size: 22420, mode: os.FileMode(420), modTime: time.Unix(1644337051, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -504,7 +483,7 @@ func xiangDataIndexHtml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "xiang/data/index.html", size: 302, mode: os.FileMode(420), modTime: time.Unix(1641540958, 0)} + info := bindataFileInfo{name: "xiang/data/index.html", size: 302, mode: os.FileMode(420), modTime: time.Unix(1644337051, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -524,7 +503,7 @@ func xiangFlowsReadmeMd() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "xiang/flows/README.md", size: 12, mode: os.FileMode(420), modTime: time.Unix(1641540958, 0)} + info := bindataFileInfo{name: "xiang/flows/README.md", size: 12, mode: os.FileMode(420), modTime: time.Unix(1644337051, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -544,7 +523,7 @@ func xiangFlowsMenuFlowJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "xiang/flows/menu.flow.json", size: 1045, mode: os.FileMode(420), modTime: time.Unix(1641540958, 0)} + info := bindataFileInfo{name: "xiang/flows/menu.flow.json", size: 1045, mode: os.FileMode(420), modTime: time.Unix(1644337051, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -564,7 +543,7 @@ func xiangFlowsTableGetFlowJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "xiang/flows/table/get.flow.json", size: 541, mode: os.FileMode(420), modTime: time.Unix(1641540958, 0)} + info := bindataFileInfo{name: "xiang/flows/table/get.flow.json", size: 541, mode: os.FileMode(420), modTime: time.Unix(1644337051, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -584,7 +563,7 @@ func xiangModelsReadmeMd() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "xiang/models/README.md", size: 15, mode: os.FileMode(420), modTime: time.Unix(1641540958, 0)} + info := bindataFileInfo{name: "xiang/models/README.md", size: 15, mode: os.FileMode(420), modTime: time.Unix(1644337051, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -604,7 +583,7 @@ func xiangModelsMenuJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "xiang/models/menu.json", size: 2378, mode: os.FileMode(420), modTime: time.Unix(1641540958, 0)} + info := bindataFileInfo{name: "xiang/models/menu.json", size: 2378, mode: os.FileMode(420), modTime: time.Unix(1644337051, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -624,7 +603,7 @@ func xiangModelsUserJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "xiang/models/user.json", size: 8826, mode: os.FileMode(420), modTime: time.Unix(1641540958, 0)} + info := bindataFileInfo{name: "xiang/models/user.json", size: 8826, mode: os.FileMode(420), modTime: time.Unix(1644337051, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -644,7 +623,7 @@ func xiangModelsWorkflowModJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "xiang/models/workflow.mod.json", size: 3333, mode: os.FileMode(420), modTime: time.Unix(1641540958, 0)} + info := bindataFileInfo{name: "xiang/models/workflow.mod.json", size: 3333, mode: os.FileMode(420), modTime: time.Unix(1644337051, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -664,7 +643,7 @@ func xiangTablesReadmeMd() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "xiang/tables/README.md", size: 15, mode: os.FileMode(420), modTime: time.Unix(1641540958, 0)} + info := bindataFileInfo{name: "xiang/tables/README.md", size: 15, mode: os.FileMode(420), modTime: time.Unix(1644337051, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -684,7 +663,7 @@ func xiangTablesMenuJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "xiang/tables/menu.json", size: 10382, mode: os.FileMode(420), modTime: time.Unix(1641540958, 0)} + info := bindataFileInfo{name: "xiang/tables/menu.json", size: 10382, mode: os.FileMode(420), modTime: time.Unix(1644337051, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -704,7 +683,7 @@ func xiangTablesUserJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "xiang/tables/user.json", size: 1422, mode: os.FileMode(420), modTime: time.Unix(1641540958, 0)} + info := bindataFileInfo{name: "xiang/tables/user.json", size: 1422, mode: os.FileMode(420), modTime: time.Unix(1644337051, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -762,16 +741,15 @@ func AssetNames() []string { // _bindata is a table, holding each asset generator, mapped to its name. var _bindata = map[string]func() (*asset, error){ "ui/index.html": uiIndexHtml, - "xiang/.DS_Store": xiangDs_store, "xiang/apis/README.md": xiangApisReadmeMd, "xiang/apis/chart.http.json": xiangApisChartHttpJson, + "xiang/apis/import.http.json": xiangApisImportHttpJson, "xiang/apis/page.http.json": xiangApisPageHttpJson, "xiang/apis/storage.http.json": xiangApisStorageHttpJson, "xiang/apis/table.http.json": xiangApisTableHttpJson, "xiang/apis/user.http.json": xiangApisUserHttpJson, "xiang/apis/xiang.http.json": xiangApisXiangHttpJson, "xiang/data/app.json": xiangDataAppJson, - "xiang/data/icons/.DS_Store": xiangDataIconsDs_store, "xiang/data/icons/404.png": xiangDataIcons404Png, "xiang/data/icons/icon.icns": xiangDataIconsIconIcns, "xiang/data/icons/icon.ico": xiangDataIconsIconIco, @@ -834,10 +812,10 @@ var _bintree = &bintree{nil, map[string]*bintree{ "index.html": {uiIndexHtml, map[string]*bintree{}}, }}, "xiang": {nil, map[string]*bintree{ - ".DS_Store": {xiangDs_store, map[string]*bintree{}}, "apis": {nil, map[string]*bintree{ "README.md": {xiangApisReadmeMd, map[string]*bintree{}}, "chart.http.json": {xiangApisChartHttpJson, map[string]*bintree{}}, + "import.http.json": {xiangApisImportHttpJson, map[string]*bintree{}}, "page.http.json": {xiangApisPageHttpJson, map[string]*bintree{}}, "storage.http.json": {xiangApisStorageHttpJson, map[string]*bintree{}}, "table.http.json": {xiangApisTableHttpJson, map[string]*bintree{}}, @@ -847,7 +825,6 @@ var _bintree = &bintree{nil, map[string]*bintree{ "data": {nil, map[string]*bintree{ "app.json": {xiangDataAppJson, map[string]*bintree{}}, "icons": {nil, map[string]*bintree{ - ".DS_Store": {xiangDataIconsDs_store, map[string]*bintree{}}, "404.png": {xiangDataIcons404Png, map[string]*bintree{}}, "icon.icns": {xiangDataIconsIconIcns, map[string]*bintree{}}, "icon.ico": {xiangDataIconsIconIco, map[string]*bintree{}}, diff --git a/engine/load.go b/engine/load.go index a59460f9..c16413e0 100644 --- a/engine/load.go +++ b/engine/load.go @@ -2,7 +2,8 @@ package engine import ( "fmt" - "strings" + "os" + "path/filepath" "github.com/yaoapp/gou" "github.com/yaoapp/kun/exception" @@ -25,11 +26,17 @@ import ( // Load 根据配置加载 API, FLow, Model, Plugin func Load(cfg config.Config) { - share.DBConnect(cfg.Database) // 创建数据库连接 + share.DBConnect(cfg.DB) // 创建数据库连接 share.SessionConnect(cfg.Session) // 创建会话服务器链接 app.Load(cfg) // 加载应用信息 - LoadEngine(cfg.Path) + + if os.Getenv("YAO_DEV") != "" { + LoadEngine(filepath.Join(os.Getenv("YAO_DEV"), "/xiang")) + } else { + LoadEngine() + } + query.Load(cfg) // 加载数据分析引擎 share.Load(cfg) // 加载共享库 lib @@ -46,7 +53,7 @@ func Load(cfg config.Config) { server.Load(cfg) // 加载服务 // 加密密钥函数 - gou.LoadCrypt(fmt.Sprintf(`{"key":"%s"}`, cfg.Database.AESKey), "AES") + gou.LoadCrypt(fmt.Sprintf(`{"key":"%s"}`, cfg.DB.AESKey), "AES") gou.LoadCrypt(`{}`, "PASSWORD") } @@ -60,15 +67,12 @@ func Reload(cfg config.Config) { } // LoadEngine 加载引擎的 API, Flow, Model 配置 -func LoadEngine(from string) { - +func LoadEngine(from ...string) { var scripts []share.Script - if strings.HasPrefix(from, "fs://") || !strings.Contains(from, "://") { - root := strings.TrimPrefix(from, "fs://") - scripts = share.GetFilesFS(root, ".json") - } else if strings.HasPrefix(from, "bin://") { - root := strings.TrimPrefix(from, "bin://") - scripts = share.GetFilesBin(root, ".json") + if len(from) > 0 { + scripts = share.GetFilesFS(from[0], ".json") + } else { + scripts = share.GetFilesBin("/xiang", ".json") } if scripts == nil { diff --git a/engine/load_test.go b/engine/load_test.go index fb306662..74368b32 100644 --- a/engine/load_test.go +++ b/engine/load_test.go @@ -1,6 +1,7 @@ package engine import ( + "os" "path" "testing" @@ -18,7 +19,7 @@ func TestLoad(t *testing.T) { // 从文件系统载入引擎文件 func TestLoadEngineFS(t *testing.T) { defer Load(config.Conf) - root := "fs://" + path.Join(config.Conf.Source, "/xiang") + root := path.Join(os.Getenv("YAO_DEV"), "/xiang") assert.NotPanics(t, func() { LoadEngine(root) }) @@ -28,8 +29,7 @@ func TestLoadEngineFS(t *testing.T) { // 从BinDataz载入引擎文件 func TestLoadEngineBin(t *testing.T) { defer Load(config.Conf) - root := "bin://xiang" assert.NotPanics(t, func() { - LoadEngine(root) + LoadEngine() }) } diff --git a/engine/process.go b/engine/process.go index e7c23064..d3cd1b11 100644 --- a/engine/process.go +++ b/engine/process.go @@ -1,6 +1,8 @@ package engine import ( + "path/filepath" + "github.com/yaoapp/gou" "github.com/yaoapp/xiang/config" "github.com/yaoapp/xiang/share" @@ -25,11 +27,10 @@ func processPing(process *gou.Process) interface{} { res := map[string]interface{}{ "code": 200, - "server": "象传应用引擎", + "server": "Yao App Engine", "version": share.VERSION, - "domain": share.DOMAIN, - "allows": config.Conf.Service.Allow, - "args": input, + // "allows": config.Conf.Service.Allow, + "args": input, } return res } @@ -60,7 +61,7 @@ func processFileContent(process *gou.Process) interface{} { // processAppFileContent 返回应用文件内容 func processAppFileContent(process *gou.Process) interface{} { process.ValidateArgNums(2) - fs := xfs.New(config.Conf.RootData) + fs := xfs.New(filepath.Join(config.Conf.Root, "data")) filename := process.ArgsString(0) encode := process.ArgsBool(1, true) content := fs.MustReadFile(filename) diff --git a/flow/flow.go b/flow/flow.go index 8773669a..7134d9e6 100644 --- a/flow/flow.go +++ b/flow/flow.go @@ -1,31 +1,45 @@ package flow import ( + "fmt" + "path/filepath" + "github.com/yaoapp/gou" + "github.com/yaoapp/kun/log" "github.com/yaoapp/xiang/config" "github.com/yaoapp/xiang/share" ) // Load 加载业务逻辑编排 -func Load(cfg config.Config) { - LoadFrom(cfg.RootFLow, "") +func Load(cfg config.Config) error { + if share.BUILDIN { + return LoadBuildIn("flows", "") + } + return LoadFrom(filepath.Join(cfg.Root, "flows"), "") } // LoadFrom 从特定目录加载 -func LoadFrom(dir string, prefix string) { +func LoadFrom(dir string, prefix string) error { if share.DirNotExists(dir) { - return + return fmt.Errorf("%s does not exists", dir) } - share.Walk(dir, ".json", func(root, filename string) { + err := share.Walk(dir, ".json", func(root, filename string) { name := prefix + share.SpecName(root, filename) content := share.ReadFile(filename) - gou.LoadFlow(string(content), name) + _, err := gou.LoadFlowReturn(string(content), name) + if err != nil { + log.With(log.F{"root": root, "file": filename}).Error(err.Error()) + } }) + if err != nil { + return err + } + // Load Script - share.Walk(dir, ".js", func(root, filename string) { + err = share.Walk(dir, ".js", func(root, filename string) { name := prefix + share.SpecName(root, filename) flow := gou.SelectFlow(name) if flow != nil { @@ -34,4 +48,11 @@ func LoadFrom(dir string, prefix string) { flow.LoadScript(string(content), script) } }) + + return err +} + +// LoadBuildIn 从制品中读取 +func LoadBuildIn(dir string, prefix string) error { + return nil } diff --git a/go.mod b/go.mod index b6ef25d8..834f5683 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f // indirect github.com/buraksezer/consistent v0.9.0 // indirect github.com/buraksezer/olric v0.4.2 - github.com/caarlos0/env/v6 v6.7.1 + github.com/caarlos0/env/v6 v6.9.1 github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/elazarl/go-bindata-assetfs v1.0.1 github.com/fatih/color v1.13.0 diff --git a/go.sum b/go.sum index ea8e072f..3e67e4d7 100644 --- a/go.sum +++ b/go.sum @@ -74,8 +74,8 @@ github.com/buraksezer/consistent v0.9.0 h1:Zfs6bX62wbP3QlbPGKUhqDw7SmNkOzY5bHZIY github.com/buraksezer/consistent v0.9.0/go.mod h1:6BrVajWq7wbKZlTOUPs/XVfR8c0maujuPowduSpZqmw= github.com/buraksezer/olric v0.4.2 h1:1W5UCYFYrkeD6OHGyTi6R9YTLJ3vXcVkgas4ZbFQMfw= github.com/buraksezer/olric v0.4.2/go.mod h1:xNt+/QiiVuqioJzgTMuiV2LynTNu87L1Tp5289XuU78= -github.com/caarlos0/env/v6 v6.7.1 h1:2r2GyonA8aJX6lDEhwFfpxwAX8Z3mvbE1X6vhaSzEyU= -github.com/caarlos0/env/v6 v6.7.1/go.mod h1:FE0jGiAnQqtv2TenJ4KTa8+/T2Ss8kdS5s1VEjasoN0= +github.com/caarlos0/env/v6 v6.9.1 h1:zOkkjM0F6ltnQ5eBX6IPI41UP/KDGEK7rRPwGCNos8k= +github.com/caarlos0/env/v6 v6.9.1/go.mod h1:hvp/ryKXKipEkcuYjs9mI4bBCg+UI0Yhgm5Zu0ddvwc= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= @@ -323,8 +323,6 @@ github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk= github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= -github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= diff --git a/helper/captcha.go b/helper/captcha.go index 69e14114..830b4fae 100644 --- a/helper/captcha.go +++ b/helper/captcha.go @@ -8,9 +8,8 @@ import ( "github.com/yaoapp/gou" "github.com/yaoapp/kun/any" "github.com/yaoapp/kun/exception" + "github.com/yaoapp/kun/log" "github.com/yaoapp/kun/maps" - "github.com/yaoapp/xiang/config" - "github.com/yaoapp/xiang/xlog" ) var captchaStore = base64Captcha.DefaultMemStore @@ -83,9 +82,7 @@ func CaptchaMake(option CaptchaOption) (string, string) { } // 打印日志 - if config.IsDebug() { - xlog.Println("图形/音频 ID:", id, "验证码:", captchaStore.Get(id, false)) - } + log.Debug("图形/音频 ID:%s 验证码:%s", id, captchaStore.Get(id, false)) return id, content } diff --git a/helper/jwt.go b/helper/jwt.go index 227dbf85..e7912fbc 100644 --- a/helper/jwt.go +++ b/helper/jwt.go @@ -29,7 +29,7 @@ type JwtToken struct { // JwtValidate JWT 校验 func JwtValidate(tokenString string) *JwtClaims { token, err := jwt.ParseWithClaims(tokenString, &JwtClaims{}, func(token *jwt.Token) (interface{}, error) { - return []byte(config.Conf.JWT.Secret), nil + return []byte(config.Conf.JWTSecret), nil }) if err != nil { @@ -94,7 +94,7 @@ func JwtMake(id int, data map[string]interface{}, option map[string]interface{}) }, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) - tokenString, err := token.SignedString([]byte(config.Conf.JWT.Secret)) + tokenString, err := token.SignedString([]byte(config.Conf.JWTSecret)) if err != nil { exception.New("生成令牌失败", 500).Ctx(err).Throw() } diff --git a/importer/init_test.go b/importer/init_test.go index 219355a1..6f5055bd 100644 --- a/importer/init_test.go +++ b/importer/init_test.go @@ -11,7 +11,7 @@ import ( ) func TestMain(m *testing.M) { - share.DBConnect(config.Conf.Database) + share.DBConnect(config.Conf.DB) model.Load(config.Conf) query.Load(config.Conf) share.Load(config.Conf) diff --git a/lang/lang.go b/lang/lang.go new file mode 100644 index 00000000..48e87f86 --- /dev/null +++ b/lang/lang.go @@ -0,0 +1 @@ +package lang diff --git a/main_test.go b/main_test.go index fc7ae493..0dee331c 100644 --- a/main_test.go +++ b/main_test.go @@ -52,7 +52,7 @@ func TestCommandStart(t *testing.T) { // 发送请求 request := func() (maps.MapStr, error) { time.Sleep(time.Microsecond * 2000) - url := fmt.Sprintf("http://%s:%d/api/user/find/1?select=id,name", "127.0.0.1", config.Conf.Service.Port) + url := fmt.Sprintf("http://%s:%d/api/user/find/1?select=id,name", "127.0.0.1", config.Conf.Port) // utils.Dump(url) resp, err := http.Get(url) if err != nil { @@ -100,7 +100,7 @@ func TestCommandStop(t *testing.T) { // 发送请求 request := func() (maps.MapStr, error) { time.Sleep(time.Microsecond * 2000) - url := fmt.Sprintf("http://%s:%d/api/user/find/1?select=id,name", "127.0.0.1", config.Conf.Service.Port) + url := fmt.Sprintf("http://%s:%d/api/user/find/1?select=id,name", "127.0.0.1", config.Conf.Port) resp, err := http.Get(url) if err != nil { return nil, err diff --git a/model/model.go b/model/model.go index cb2e5e05..91efbd0e 100644 --- a/model/model.go +++ b/model/model.go @@ -1,26 +1,43 @@ package model import ( + "fmt" + "path/filepath" + "github.com/yaoapp/gou" + "github.com/yaoapp/kun/log" "github.com/yaoapp/xiang/config" "github.com/yaoapp/xiang/share" ) // Load 加载数据模型 -func Load(cfg config.Config) { - LoadFrom(cfg.RootModel, "") +func Load(cfg config.Config) error { + if share.BUILDIN { + return LoadBuildIn("models", "") + } + return LoadFrom(filepath.Join(cfg.Root, "models"), "") } // LoadFrom 从特定目录加载 -func LoadFrom(dir string, prefix string) { +func LoadFrom(dir string, prefix string) error { if share.DirNotExists(dir) { - return + return fmt.Errorf("%s does not exists", dir) } - share.Walk(dir, ".json", func(root, filename string) { + err := share.Walk(dir, ".json", func(root, filename string) { name := prefix + share.SpecName(root, filename) content := share.ReadFile(filename) - gou.LoadModel(string(content), name) + _, err := gou.LoadModelReturn(string(content), name) + if err != nil { + log.With(log.F{"root": root, "file": filename}).Error(err.Error()) + } }) + + return err +} + +// LoadBuildIn 从制品中读取 +func LoadBuildIn(dir string, prefix string) error { + return nil } diff --git a/model/model_test.go b/model/model_test.go index 2bc5ab49..39ccfd78 100644 --- a/model/model_test.go +++ b/model/model_test.go @@ -11,7 +11,7 @@ import ( ) func TestLoad(t *testing.T) { - share.DBConnect(config.Conf.Database) + share.DBConnect(config.Conf.DB) gou.Models = make(map[string]*gou.Model) Load(config.Conf) LoadFrom("not a path", "404.") diff --git a/page/page.go b/page/page.go index 1c622d29..b9333b6a 100644 --- a/page/page.go +++ b/page/page.go @@ -7,39 +7,64 @@ import ( jsoniter "github.com/json-iterator/go" "github.com/yaoapp/gou" "github.com/yaoapp/kun/exception" + "github.com/yaoapp/kun/log" "github.com/yaoapp/xiang/config" "github.com/yaoapp/xiang/share" - "github.com/yaoapp/xiang/xlog" ) // Pages 已载入页面 var Pages = map[string]*Page{} // Load 加载页面 -func Load(cfg config.Config) { - LoadFrom(path.Join(cfg.Root, "/kanban"), "") - LoadFrom(path.Join(cfg.Root, "/screen"), "") - LoadFrom(cfg.RootPage, "") +func Load(cfg config.Config) error { + + if share.BUILDIN { + return LoadBuildIn("pages", "") + } + + var errs error = nil + if err := LoadFrom(path.Join(cfg.Root, "/kanban"), ""); err != nil { + log.Warn("load kanban error: %s ", err.Error()) + errs = err + } + if err := LoadFrom(path.Join(cfg.Root, "/screen"), ""); err != nil { + log.Warn("load screen error: %s", err.Error()) + errs = err + } + if err := LoadFrom(path.Join(cfg.Root, "/pages"), ""); err != nil { + log.Warn("load screen error: %s", err.Error()) + errs = err + } + + return errs +} + +// LoadBuildIn 从制品中读取 +func LoadBuildIn(dir string, prefix string) error { + return nil } // LoadFrom 从特定目录加载 -func LoadFrom(dir string, prefix string) { +func LoadFrom(dir string, prefix string) error { if share.DirNotExists(dir) { - return + return fmt.Errorf("%s does not exists", dir) } - share.Walk(dir, ".json", func(root, filename string) { + err := share.Walk(dir, ".json", func(root, filename string) { name := prefix + share.SpecName(root, filename) content := share.ReadFile(filename) _, err := LoadPage(content, name) if err != nil { - exception.New("%s 页面格式错误", 400, name).Ctx(filename).Throw() + log.With(log.F{"root": root, "file": filename}).Error(err.Error()) } }) + if err != nil { + return err + } // Load Script - share.Walk(dir, ".js", func(root, filename string) { + err = share.Walk(dir, ".js", func(root, filename string) { name := prefix + share.SpecName(root, filename) page := Select(name) if page != nil { @@ -49,6 +74,8 @@ func LoadFrom(dir string, prefix string) { } }) + return err + } // LoadPage 载入页面 @@ -60,9 +87,7 @@ func LoadPage(source []byte, name string) (*Page, error) { } err := jsoniter.Unmarshal(source, page) if err != nil { - xlog.Println(name) - xlog.Println(err.Error()) - xlog.Println(string(source)) + log.With(log.F{"name": name, "source": source}).Error(err.Error()) return nil, err } page.Prepare() diff --git a/page/page_test.go b/page/page_test.go index 507906c6..c5647595 100644 --- a/page/page_test.go +++ b/page/page_test.go @@ -11,7 +11,7 @@ import ( ) func TestLoad(t *testing.T) { - share.DBConnect(config.Conf.Database) + share.DBConnect(config.Conf.DB) model.Load(config.Conf) query.Load(config.Conf) diff --git a/page/process_test.go b/page/process_test.go index 973fad16..3f4a6580 100644 --- a/page/process_test.go +++ b/page/process_test.go @@ -17,7 +17,7 @@ import ( ) func init() { - share.DBConnect(config.Conf.Database) + share.DBConnect(config.Conf.DB) model.Load(config.Conf) query.Load(config.Conf) Load(config.Conf) diff --git a/plugin/plugin.go b/plugin/plugin.go index 7a5866e0..c237fcda 100644 --- a/plugin/plugin.go +++ b/plugin/plugin.go @@ -1,25 +1,33 @@ package plugin import ( + "fmt" + "path/filepath" + "github.com/yaoapp/gou" + "github.com/yaoapp/kun/log" "github.com/yaoapp/xiang/config" "github.com/yaoapp/xiang/share" ) // Load 加载业务插件 -func Load(cfg config.Config) { - LoadFrom(cfg.RootPlugin) +func Load(cfg config.Config) error { + return LoadFrom(filepath.Join(cfg.Root, "plugins")) } // LoadFrom 从特定目录加载 -func LoadFrom(dir string) { +func LoadFrom(dir string) error { if share.DirNotExists(dir) { - return + return fmt.Errorf("%s does not exists", dir) } - share.Walk(dir, ".so", func(root, filename string) { + err := share.Walk(dir, ".so", func(root, filename string) { name := share.SpecName(root, filename) - gou.LoadPlugin(filename, name) + _, err := gou.LoadPluginReturn(filename, name) + if err != nil { + log.With(log.F{"root": root, "file": filename}).Error(err.Error()) + } }) + return err } diff --git a/query/query.go b/query/query.go index 8c19ba6c..94939572 100644 --- a/query/query.go +++ b/query/query.go @@ -3,7 +3,7 @@ package query import ( "github.com/yaoapp/gou" dsl "github.com/yaoapp/gou/query/gou" - "github.com/yaoapp/kun/exception" + "github.com/yaoapp/kun/log" "github.com/yaoapp/xiang/config" "github.com/yaoapp/xun/capsule" ) @@ -21,9 +21,10 @@ func XiangQuery() { if mod, has := gou.Models[s]; has { return mod.MetaData.Table.Name } - exception.New("%s 数据模型尚未加载", 404).Throw() + // exception.New("%s 数据模型尚未加载", 404).Throw() + log.Error("%s model does not load", s) return s }, - AESKey: config.Conf.Database.AESKey, + AESKey: config.Conf.DB.AESKey, }) } diff --git a/server/socket.go b/server/socket.go index 9f0bd6f0..c5ce68aa 100644 --- a/server/socket.go +++ b/server/socket.go @@ -1,29 +1,36 @@ package server import ( + "fmt" "path/filepath" "github.com/yaoapp/gou" + "github.com/yaoapp/kun/log" "github.com/yaoapp/xiang/config" "github.com/yaoapp/xiang/share" ) // Load 加载API func Load(cfg config.Config) { - var root = filepath.Join(cfg.RootAPI, "..", "servers") + var root = filepath.Join(cfg.Root, "servers") LoadFrom(root, "") } // LoadFrom 从特定目录加载 -func LoadFrom(dir string, prefix string) { +func LoadFrom(dir string, prefix string) error { if share.DirNotExists(dir) { - return + return fmt.Errorf("%s does not exists", dir) } - share.Walk(dir, ".sock.json", func(root, filename string) { + err := share.Walk(dir, ".sock.json", func(root, filename string) { name := prefix + share.SpecName(root, filename) content := share.ReadFile(filename) - gou.LoadServer(string(content), name) + _, err := gou.LoadServer(string(content), name) + if err != nil { + log.With(log.F{"root": root, "file": filename}).Error(err.Error()) + } }) + + return err } diff --git a/service/guard.go b/service/guard.go index c12db34e..79a51ded 100644 --- a/service/guard.go +++ b/service/guard.go @@ -28,10 +28,10 @@ func bearerJWT(c *gin.Context) { tokenString = strings.TrimSpace(strings.TrimPrefix(tokenString, "Bearer ")) if config.Conf.Mode == "debug" { - xlog.Printf("JWT: %s Secret: %s", tokenString, config.Conf.JWT.Secret) + xlog.Printf("JWT: %s Secret: %s", tokenString, config.Conf.JWTSecret) } token, err := jwt.ParseWithClaims(tokenString, &helper.JwtClaims{}, func(token *jwt.Token) (interface{}, error) { - return []byte(config.Conf.JWT.Secret), nil + return []byte(config.Conf.JWTSecret), nil }) if err != nil { diff --git a/service/middleware.go b/service/middleware.go index 025075fe..6b2fe7c0 100644 --- a/service/middleware.go +++ b/service/middleware.go @@ -1,21 +1,20 @@ package service import ( - "fmt" "net/http" + "path/filepath" "strings" "github.com/gin-gonic/gin" "github.com/yaoapp/xiang/config" "github.com/yaoapp/xiang/data" - "github.com/yaoapp/xiang/share" ) // AdminFileServer 数据管理平台 var AdminFileServer http.Handler = http.FileServer(data.AssetFS()) // AppFileServer 应用静态文件 -var AppFileServer http.Handler = http.FileServer(http.Dir(config.Conf.RootUI)) +var AppFileServer http.Handler = http.FileServer(http.Dir(filepath.Join(config.Conf.Root, "ui"))) // Middlewares 服务中间件 var Middlewares = []gin.HandlerFunc{ @@ -23,23 +22,6 @@ var Middlewares = []gin.HandlerFunc{ BinStatic, } -// BindDomain 绑定许可域名 -func BindDomain(c *gin.Context) { - - for _, allow := range share.AllowHosts { - if strings.Contains(c.Request.Host, allow) { - c.Next() - return - } - } - - c.JSON(403, gin.H{ - "code": 403, - "message": fmt.Sprintf("%s is not allowed", c.Request.Host), - }) - c.Abort() -} - // BinStatic 静态文件服务 func BinStatic(c *gin.Context) { diff --git a/service/service.go b/service/service.go index 11cf8d8b..d10c443c 100644 --- a/service/service.go +++ b/service/service.go @@ -19,10 +19,9 @@ func Start() { gou.SetHTTPGuards(Guards) gou.ServeHTTP( gou.Server{ - Host: config.Conf.Service.Host, - Port: config.Conf.Service.Port, - Allows: config.Conf.Service.Allow, - Root: "/api", + Host: config.Conf.Host, + Port: config.Conf.Port, + Root: "/api", }, &shutdown, func(s gou.Server) { shutdownComplete <- true diff --git a/service/watch.go b/service/watch.go index 6d08b57b..7e556cf7 100644 --- a/service/watch.go +++ b/service/watch.go @@ -17,15 +17,15 @@ import ( // Watch 监听应用目录文件变更 func Watch(cfg config.Config) { - WatchEngine(cfg.Path) - WatchModel(cfg.RootModel, "") - WatchAPI(cfg.RootAPI, "") - WatchFlow(cfg.RootFLow, "") - WatchPlugin(cfg.RootPlugin) - WatchTable(cfg.RootTable, "") - WatchChart(cfg.RootChart, "") - WatchPage(cfg.RootPage, "") - WatchWorkFlow(cfg.RootWorkFlow, "") + // WatchEngine(cfg.Path) + // WatchModel(cfg.RootModel, "") + // WatchAPI(cfg.RootAPI, "") + // WatchFlow(cfg.RootFLow, "") + // WatchPlugin(cfg.RootPlugin) + // WatchTable(cfg.RootTable, "") + // WatchChart(cfg.RootChart, "") + // WatchPage(cfg.RootPage, "") + // WatchWorkFlow(cfg.RootWorkFlow, "") // 看板大屏 WatchPage(filepath.Join(cfg.Root, "/kanban"), "") diff --git a/share/const.go b/share/const.go index 0946531f..b2d6c43d 100644 --- a/share/const.go +++ b/share/const.go @@ -3,8 +3,8 @@ package share // VERSION 版本号 const VERSION = "0.8.0" -// DOMAIN 许可域(废弃) -const DOMAIN = "*.iqka.com" +// BUILDIN 应用打包成一个制品 +const BUILDIN = false -// AllowHosts 解析后的许可域(废弃) -var AllowHosts = []string{} +// BUILDNAME 制品名称 +const BUILDNAME = "yao" diff --git a/share/db.go b/share/db.go index 6582d31a..98bff706 100644 --- a/share/db.go +++ b/share/db.go @@ -8,7 +8,7 @@ import ( ) // DBConnect 建立数据库连接 -func DBConnect(dbconfig config.DatabaseConfig) { +func DBConnect(dbconfig config.DBConfig) { // 连接主库 for i, dsn := range dbconfig.Primary { diff --git a/share/importable.go b/share/importable.go index d09f982b..16cd1378 100644 --- a/share/importable.go +++ b/share/importable.go @@ -2,6 +2,7 @@ package share import ( "fmt" + "path/filepath" jsoniter "github.com/json-iterator/go" "github.com/yaoapp/gou" @@ -17,7 +18,7 @@ var Libs = map[string]map[string]interface{}{} // Load 加载共享库 func Load(cfg config.Config) { - LoadFrom(cfg.RootLib) + LoadFrom(filepath.Join(cfg.Root, "libs")) } // LoadFrom 从特定目录加载共享库 diff --git a/share/importable_test.go b/share/importable_test.go index 6102400e..8f760650 100644 --- a/share/importable_test.go +++ b/share/importable_test.go @@ -1,17 +1,17 @@ package share import ( + "os" "path" "testing" jsoniter "github.com/json-iterator/go" "github.com/stretchr/testify/assert" "github.com/yaoapp/gou" - "github.com/yaoapp/xiang/config" ) func init() { - rootLib := path.Join(config.Conf.Source, "/tests/libs") + rootLib := path.Join(os.Getenv("YAO_DEV"), "/tests/libs") LoadFrom(rootLib) } diff --git a/share/lang.go b/share/lang.go new file mode 100644 index 00000000..4f6b9b37 --- /dev/null +++ b/share/lang.go @@ -0,0 +1,6 @@ +package share + +// Langs 多语言包 +var Langs = map[string]string{ + "象传应用引擎": "Yao App Engine", +} diff --git a/share/utils.go b/share/utils.go index e5a7dbcd..62723090 100644 --- a/share/utils.go +++ b/share/utils.go @@ -2,24 +2,24 @@ package share import ( "io/ioutil" - "log" "os" "path" "path/filepath" "strings" "github.com/yaoapp/kun/exception" + "github.com/yaoapp/kun/log" "github.com/yaoapp/xiang/data" ) // Walk 遍历应用目录,读取文件列表 -func Walk(root string, typeName string, cb func(root, filename string)) { +func Walk(root string, typeName string, cb func(root, filename string)) error { root = strings.TrimPrefix(root, "fs://") root = strings.TrimPrefix(root, "file://") root = path.Join(root, "/") - filepath.Walk(root, func(filename string, info os.FileInfo, err error) error { + err := filepath.Walk(root, func(filename string, info os.FileInfo, err error) error { if err != nil { - exception.Err(err, 500).Throw() + log.With(log.F{"root": root, "type": typeName, "filename": filename}).Error(err.Error()) return err } if strings.HasSuffix(filename, typeName) { @@ -27,6 +27,7 @@ func Walk(root string, typeName string, cb func(root, filename string)) { } return nil }) + return err } // SpecName 解析名称 root: "/tests/apis" file: "/tests/apis/foo/bar.http.json" @@ -78,7 +79,7 @@ func DirAbs(dir string) string { dir = strings.TrimPrefix(dir, "file://") dirAbs, err := filepath.Abs(dir) if err != nil { - log.Panicf("获取绝对路径错误 %s %s", dir, err) + log.Panic("获取绝对路径错误 %s %s", dir, err) } return dirAbs } diff --git a/share/watch_test.go b/share/watch_test.go index 3fe472b3..bc6e49e6 100644 --- a/share/watch_test.go +++ b/share/watch_test.go @@ -2,16 +2,16 @@ package share import ( "log" + "os" "path" "testing" "time" "github.com/stretchr/testify/assert" - "github.com/yaoapp/xiang/config" ) func TestWatch(t *testing.T) { - root := path.Join(config.Conf.Source, "/tests/flows") + root := path.Join(os.Getenv("YAO_DEV"), "/tests/flows") assert.NotPanics(t, func() { go Watch(root, func(op string, file string) { log.Println(op, file) diff --git a/table/process_test.go b/table/process_test.go index 610e6a68..c88b0c24 100644 --- a/table/process_test.go +++ b/table/process_test.go @@ -20,7 +20,7 @@ import ( ) func init() { - share.DBConnect(config.Conf.Database) + share.DBConnect(config.Conf.DB) model.Load(config.Conf) share.Load(config.Conf) query.Load(config.Conf) diff --git a/table/table.go b/table/table.go index 468bb1a1..181303b7 100644 --- a/table/table.go +++ b/table/table.go @@ -4,11 +4,13 @@ import ( "fmt" "io" "os" + "path/filepath" "strings" "github.com/yaoapp/gou" "github.com/yaoapp/gou/helper" "github.com/yaoapp/kun/exception" + "github.com/yaoapp/kun/log" "github.com/yaoapp/kun/maps" "github.com/yaoapp/xiang/config" "github.com/yaoapp/xiang/share" @@ -19,33 +21,46 @@ import ( var Tables = map[string]*Table{} // Load 加载数据表格 -func Load(cfg config.Config) { - LoadFrom(cfg.RootTable, "") +func Load(cfg config.Config) error { + if share.BUILDIN { + return LoadBuildIn("tables", "") + } + return LoadFrom(filepath.Join(cfg.Root, "tables"), "") } // LoadFrom 从特定目录加载 -func LoadFrom(dir string, prefix string) { +func LoadFrom(dir string, prefix string) error { if share.DirNotExists(dir) { - return + return fmt.Errorf("%s does not exists", dir) } - share.Walk(dir, ".json", func(root, filename string) { + err := share.Walk(dir, ".json", func(root, filename string) { name := share.SpecName(root, filename) content := share.ReadFile(filename) - LoadTable(string(content), name) + _, err := LoadTable(string(content), name) + if err != nil { + log.With(log.F{"root": root, "file": filename}).Error(err.Error()) + } }) + + return err +} + +// LoadBuildIn 从制品中读取 +func LoadBuildIn(dir string, prefix string) error { + return nil } // LoadTable 载入数据表格 -func LoadTable(source string, name string) *Table { +func LoadTable(source string, name string) (*Table, error) { var input io.Reader = nil if strings.HasPrefix(source, "file://") || strings.HasPrefix(source, "fs://") { filename := strings.TrimPrefix(source, "file://") filename = strings.TrimPrefix(filename, "fs://") file, err := os.Open(filename) if err != nil { - exception.Err(err, 400).Throw() + return nil, err } defer file.Close() input = file @@ -66,7 +81,7 @@ func LoadTable(source string, name string) *Table { table.loadFilters() table.loadAPIs() Tables[name] = &table - return Tables[name] + return Tables[name], nil } // Select 读取已加载表格配置 @@ -82,9 +97,13 @@ func Select(name string) *Table { } // Reload 更新数据表格配置 -func (table *Table) Reload() *Table { - *table = *LoadTable(table.Source, table.Name) - return table +func (table *Table) Reload() (*Table, error) { + new, err := LoadTable(table.Source, table.Name) + if err != nil { + return nil, err + } + *table = *new + return table, nil } // loadAPIs 加载数据管理 API diff --git a/table/table_test.go b/table/table_test.go index e56e8060..5f27834b 100644 --- a/table/table_test.go +++ b/table/table_test.go @@ -11,7 +11,7 @@ import ( ) func TestLoad(t *testing.T) { - share.DBConnect(config.Conf.Database) + share.DBConnect(config.Conf.DB) share.Load(config.Conf) model.Load(config.Conf) diff --git a/tests/locales/en.json b/tests/locales/en.json new file mode 100644 index 00000000..0eeffd75 --- /dev/null +++ b/tests/locales/en.json @@ -0,0 +1,4 @@ +{ + "Yao": "Yao", + "象传应用引擎": "Yao App Engine" +} diff --git a/user/captcha.go b/user/captcha.go index 334c629d..ceaa5842 100644 --- a/user/captcha.go +++ b/user/captcha.go @@ -6,8 +6,7 @@ import ( "github.com/mojocn/base64Captcha" "github.com/yaoapp/kun/exception" - "github.com/yaoapp/xiang/config" - "github.com/yaoapp/xiang/xlog" + "github.com/yaoapp/kun/log" ) var captchaStore = base64Captcha.DefaultMemStore @@ -79,11 +78,7 @@ func MakeCaptcha(option CaptchaOption) (string, string) { exception.New("生成验证码出错 %s", 500, err).Throw() } - // 打印日志 - if config.IsDebug() { - xlog.Println("图形/音频 ID:", id, "验证码:", captchaStore.Get(id, false)) - } - + log.Debug("图形/音频 ID:%s 验证码:%s", id, captchaStore.Get(id, false)) return id, content } diff --git a/user/process.go b/user/process.go index 0d6e0acd..d5e896d6 100644 --- a/user/process.go +++ b/user/process.go @@ -4,10 +4,9 @@ import ( "github.com/yaoapp/gou" "github.com/yaoapp/kun/any" "github.com/yaoapp/kun/exception" + "github.com/yaoapp/kun/log" "github.com/yaoapp/kun/maps" "github.com/yaoapp/kun/utils" - "github.com/yaoapp/xiang/config" - "github.com/yaoapp/xiang/xlog" ) func init() { @@ -19,9 +18,7 @@ func init() { func ProcessLogin(process *gou.Process) interface{} { process.ValidateArgNums(1) payload := process.ArgsMap(0).Dot() - if config.IsDebug() { - xlog.Println(payload) - } + log.With(log.F{"payload": payload}).Debug("ProcessLogin") id := any.Of(payload.Get("captcha.id")).CString() value := any.Of(payload.Get("captcha.code")).CString() @@ -32,9 +29,7 @@ func ProcessLogin(process *gou.Process) interface{} { exception.New("请输入验证码", 400).Ctx(maps.Map{"id": id, "code": value}).Throw() } if !ValidateCaptcha(id, value) { - if config.IsDebug() { - xlog.Println("ID:", id, " Code:", value) - } + log.With(log.F{"id": id, "code": value}).Debug("ProcessLogin") exception.New("验证码不正确", 403).Ctx(maps.Map{"id": id, "code": value}).Throw() return nil } diff --git a/workflow/process_test.go b/workflow/process_test.go index 89b5a0f4..f3395a65 100644 --- a/workflow/process_test.go +++ b/workflow/process_test.go @@ -1,6 +1,7 @@ package workflow import ( + "os" "path" "testing" @@ -16,10 +17,10 @@ import ( ) func init() { - share.DBConnect(config.Conf.Database) + share.DBConnect(config.Conf.DB) share.Load(config.Conf) model.Load(config.Conf) - engineModels := path.Join(config.Conf.Source, "xiang", "models") + engineModels := path.Join(os.Getenv("YAO_DEV"), "xiang", "models") model.LoadFrom(engineModels, "xiang.") query.Load(config.Conf) flow.Load(config.Conf) diff --git a/workflow/workflow.go b/workflow/workflow.go index a2c48899..bf02acf7 100644 --- a/workflow/workflow.go +++ b/workflow/workflow.go @@ -2,6 +2,7 @@ package workflow import ( "fmt" + "path/filepath" "strings" jsoniter "github.com/json-iterator/go" @@ -9,6 +10,7 @@ import ( gshare "github.com/yaoapp/gou/query/share" "github.com/yaoapp/kun/any" "github.com/yaoapp/kun/exception" + "github.com/yaoapp/kun/log" "github.com/yaoapp/kun/maps" "github.com/yaoapp/kun/str" "github.com/yaoapp/xiang/config" @@ -39,7 +41,7 @@ var WorkFlows = map[string]*WorkFlow{} // Load 加载数据表格 func Load(cfg config.Config) { - LoadFrom(cfg.RootWorkFlow, "") + LoadFrom(filepath.Join(cfg.Root, "workflows"), "") } // LoadFrom 从特定目录加载 @@ -52,7 +54,7 @@ func LoadFrom(dir string, prefix string) { content := share.ReadFile(filename) _, err := LoadWorkFlow(content, name) if err != nil { - exception.New("%s 工作流格式错误", 400, name).Ctx(filename).Throw() + log.With(log.F{"root": root, "filename": filename}).Error(err.Error()) } }) } diff --git a/workflow/workflow_test.go b/workflow/workflow_test.go index 271dbe09..c1087a3b 100644 --- a/workflow/workflow_test.go +++ b/workflow/workflow_test.go @@ -1,6 +1,7 @@ package workflow import ( + "os" "path" "testing" @@ -16,10 +17,10 @@ import ( ) func init() { - share.DBConnect(config.Conf.Database) + share.DBConnect(config.Conf.DB) share.Load(config.Conf) model.Load(config.Conf) - engineModels := path.Join(config.Conf.Source, "xiang", "models") + engineModels := path.Join(os.Getenv("YAO_DEV"), "xiang", "models") model.LoadFrom(engineModels, "xiang.") query.Load(config.Conf) flow.Load(config.Conf) @@ -27,7 +28,7 @@ func init() { } func TestLoad(t *testing.T) { - share.DBConnect(config.Conf.Database) + share.DBConnect(config.Conf.DB) share.Load(config.Conf) model.Load(config.Conf) query.Load(config.Conf) diff --git a/xfs/xfs.go b/xfs/xfs.go index 008b8a3c..91edf024 100644 --- a/xfs/xfs.go +++ b/xfs/xfs.go @@ -39,7 +39,7 @@ type File interface { } func init() { - Stor = New(config.Conf.RootData) + Stor = New(filepath.Join(config.Conf.Root, "data")) } // New 创建文件系统 diff --git a/xfs/xfs_test.go b/xfs/xfs_test.go index 81926301..e063cc61 100644 --- a/xfs/xfs_test.go +++ b/xfs/xfs_test.go @@ -5,11 +5,11 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/yaoapp/xiang/config" ) func TestOSFsMustMkdirAll(t *testing.T) { - fs := New(config.Conf.Source) + // fs := New(config.Conf.Source) + fs := New(os.Getenv("YAO_DEV")) assert.NotPanics(t, func() { fs.MustMkdirAll("/.tmp/unit-test/dir", os.ModePerm) })