package config import ( "io" "log" "os" "path/filepath" "strings" "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"` } // 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 开关 Hosting bool `json:"hosting,omitempty" env:"XIANG_SESSION_HOSTING" envDefault:"true"` // 会话服务器 IsCLI bool `json:"iscli,omitempty" env:"XIANG_SESSION_ISCLI" envDefault:"false"` // 是否为客户端启动 Host string `json:"host,omitempty" env:"XIANG_SESSION_HOST" envDefault:"127.0.0.1"` // 会话服务器IP 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 } // 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 密钥 } // 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"` // 插件日志 } // NewConfig 创建配置文件 func NewConfig(envfile ...string) Config { filename := os.Getenv("XIANG_ENV_FILE") if filename == "" { filename = ".env" } 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 } } godotenv.Overload(filename) // if err != nil { // log.Printf("加载环境配置文件%s出错 %s\n", filename, err.Error()) // } cfg := Config{} if err := env.Parse(&cfg); err != nil { exception.New("解析配置文件出错 %s", 500, err.Error()).Throw() } cfg.SetDefaults() return cfg } // 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 } // SetDefaults 设定默认值 func (cfg *Config) SetDefaults() { pwd, err := os.Getwd() if err != nil { log.Fatal(err) } if cfg.Root == "fs://." || cfg.Root == "." { cfg.Root = pwd } 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.RootTable == "" { cfg.RootTable = cfg.Root + "/tables" } if cfg.RootLib == "" { cfg.RootLib = cfg.Root + "/libs" } if cfg.RootChart == "" { cfg.RootChart = cfg.Root + "/charts" } if cfg.RootPage == "" { cfg.RootPage = cfg.Root + "/pages" } if cfg.RootWorkFlow == "" { cfg.RootWorkFlow = cfg.Root + "/workflows" } if cfg.RootData == "" { cfg.RootData = cfg.Root + "/data" } if cfg.RootDB == "" { cfg.RootDB = cfg.Root + "/db" } if cfg.RootUI == "" { cfg.RootUI = cfg.Root + "/ui" } 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) { fullpath, err := filepath.Abs(root) if err != nil { log.Panicf("设定应用目录%s出错 %s\n", root, err.Error()) } // 创建应用目录 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) } if !pathinfo.IsDir() { log.Panicf("应用目录不是文件夹(%s) ", root) } // 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() }