diff --git a/cmd/server/main.go b/cmd/server/main.go index 659c77a..e19149a 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -216,7 +216,7 @@ func install() { if err := model.UseDB(db, cfg.DB.Database); err != nil { panic(err) } - if err := model.ImportSQL(db, database.GoploySQL); err != nil { + if err := model.ImportSQL(&model.Model{DB: db}, database.GoploySQL); err != nil { panic(err) } println("Database installation is complete") diff --git a/config/app.go b/config/app.go index de798b6..d5fd826 100644 --- a/config/app.go +++ b/config/app.go @@ -4,5 +4,27 @@ package config +import ( + "time" +) + const NamespaceHeaderName = "G-N-ID" const ApiKeyHeaderName = "X-API-KEY" + +type APPConfig struct { + DeployLimit int32 `toml:"deployLimit"` + ShutdownTimeout time.Duration `toml:"shutdownTimeout"` + RepositoryPath string `toml:"repositoryPath"` + PasswordPeriod int `toml:"passwordPeriod"` +} + +func (a *APPConfig) OnChange() error { + setAPPDefault() + return nil +} + +func setAPPDefault() { + if Toml.APP.ShutdownTimeout == 0 { + Toml.APP.ShutdownTimeout = 10 + } +} diff --git a/config/cache.go b/config/cache.go new file mode 100644 index 0000000..6ee3515 --- /dev/null +++ b/config/cache.go @@ -0,0 +1,5 @@ +package config + +type CacheConfig struct { + Type string `toml:"type"` +} diff --git a/config/captcha.go b/config/captcha.go new file mode 100644 index 0000000..26381ca --- /dev/null +++ b/config/captcha.go @@ -0,0 +1,5 @@ +package config + +type CaptchaConfig struct { + Enabled bool `toml:"enabled"` +} diff --git a/config/cookie.go b/config/cookie.go new file mode 100644 index 0000000..f6ea00d --- /dev/null +++ b/config/cookie.go @@ -0,0 +1,6 @@ +package config + +type CookieConfig struct { + Name string `toml:"name"` + Expire int `toml:"expire"` // second +} diff --git a/config/cors.go b/config/cors.go new file mode 100644 index 0000000..9b120d6 --- /dev/null +++ b/config/cors.go @@ -0,0 +1,9 @@ +package config + +type CORSConfig struct { + Enabled bool `toml:"enabled"` + Origins string `toml:"origins"` + Methods string `toml:"methods"` + Headers string `toml:"headers"` + Credentials bool `toml:"credentials"` +} diff --git a/config/dingtalk.go b/config/dingtalk.go new file mode 100644 index 0000000..b7cef81 --- /dev/null +++ b/config/dingtalk.go @@ -0,0 +1,6 @@ +package config + +type DingtalkConfig struct { + AppKey string `toml:"appKey"` + AppSecret string `toml:"appSecret"` +} diff --git a/config/events.go b/config/events.go new file mode 100644 index 0000000..da81f0f --- /dev/null +++ b/config/events.go @@ -0,0 +1,105 @@ +package config + +import ( + "errors" + "fmt" + log "github.com/sirupsen/logrus" + "sync" +) + +const DBEventTopic = "db_config" +const LogEventTopic = "log_config" +const APPEventTopic = "app_config" + +type Event struct { + Topic string + Val interface{} +} + +type Observer interface { + OnChange() error +} + +type EventBus struct { + mux sync.RWMutex + observers map[string]map[Observer]struct{} +} + +type BaseObserver struct { +} + +func (b *BaseObserver) OnChange(e *Event) error { + log.Printf("observer: %s, event key: %s, event val: %v", b, e.Topic, e.Val) + return nil +} + +var eventBus = &EventBus{ + observers: make(map[string]map[Observer]struct{}), +} + +func GetEventBus() *EventBus { + return eventBus +} + +func (s *EventBus) Subscribe(topic string, o Observer) { + s.mux.Lock() + defer s.mux.Unlock() + _, ok := s.observers[topic] + if !ok { + s.observers[topic] = make(map[Observer]struct{}) + } + s.observers[topic][o] = struct{}{} +} + +func (s *EventBus) Unsubscribe(topic string, o Observer) { + s.mux.Lock() + defer s.mux.Unlock() + delete(s.observers[topic], o) +} + +func (s *EventBus) Publish(e *Event) error { + s.mux.RLock() + defer s.mux.RUnlock() + subscribers := s.observers[e.Topic] + + errs := make(map[Observer]error) + for subscriber := range subscribers { + if err := subscriber.OnChange(); err != nil { + errs[subscriber] = err + } + } + + return s.handleErr(errs) +} + +func (s *EventBus) handleErr(errs map[Observer]error) error { + if len(errs) > 0 { + message := "" + for o, err := range errs { + message += fmt.Sprintf("observer: %v, err: %v;", o, err) + } + + return errors.New(message) + } + + return nil +} + +func PublishEvents(newConfig Config, topics []string) error { + errMsg := "" + for _, topic := range topics { + err := eventBus.Publish(&Event{ + Topic: topic, + Val: newConfig, + }) + if err != nil { + errMsg += err.Error() + } + } + + if errMsg != "" { + return errors.New(errMsg) + } + + return nil +} diff --git a/config/feishu.go b/config/feishu.go new file mode 100644 index 0000000..98b64ae --- /dev/null +++ b/config/feishu.go @@ -0,0 +1,6 @@ +package config + +type FeishuConfig struct { + AppKey string `toml:"appKey"` + AppSecret string `toml:"appSecret"` +} diff --git a/config/jwt.go b/config/jwt.go new file mode 100644 index 0000000..b32c4af --- /dev/null +++ b/config/jwt.go @@ -0,0 +1,5 @@ +package config + +type JWTConfig struct { + Key string `toml:"key"` +} diff --git a/config/ldap.go b/config/ldap.go new file mode 100644 index 0000000..0d06749 --- /dev/null +++ b/config/ldap.go @@ -0,0 +1,12 @@ +package config + +type LDAPConfig struct { + Enabled bool `toml:"enabled"` + URL string `toml:"url"` + BindDN string `toml:"bindDN"` + Password string `toml:"password"` + BaseDN string `toml:"baseDN"` + UID string `toml:"uid"` + Name string `toml:"name"` + UserFilter string `toml:"userFilter"` +} diff --git a/config/log.go b/config/log.go new file mode 100644 index 0000000..4fc4262 --- /dev/null +++ b/config/log.go @@ -0,0 +1,55 @@ +package config + +import ( + "fmt" + log "github.com/sirupsen/logrus" + "io" + "os" + "path" + "path/filepath" + "runtime" + "strings" +) + +type LogConfig struct { + Path string `toml:"path"` +} + +func (l *LogConfig) OnChange() error { + setLogger() + return nil +} + +func setLogger() { + var logFile io.Writer + logPathEnv := Toml.Log.Path + if strings.ToLower(logPathEnv) == "stdout" { + logFile = os.Stdout + } else { + logPath, err := filepath.Abs(logPathEnv) + if err != nil { + fmt.Println(err.Error()) + } + if _, err := os.Stat(logPath); err != nil && os.IsNotExist(err) { + if err := os.Mkdir(logPath, os.ModePerm); nil != err { + panic(err.Error()) + } + } + logFile, err = os.OpenFile(logPath+"/goploy.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0766) + if nil != err { + panic(err.Error()) + } + } + log.SetReportCaller(true) + + log.SetFormatter(&log.TextFormatter{ + CallerPrettyfier: func(f *runtime.Frame) (string, string) { + return fmt.Sprintf("%s()", path.Base(f.Function)), fmt.Sprintf("%s:%d", path.Base(f.File), f.Line) + }, + }) + + log.SetOutput(logFile) + + log.SetLevel(log.TraceLevel) + +} diff --git a/config/mysql.go b/config/mysql.go new file mode 100644 index 0000000..eb4d617 --- /dev/null +++ b/config/mysql.go @@ -0,0 +1,30 @@ +package config + +type DBConfig struct { + Type string `toml:"type"` + User string `toml:"user"` + Password string `toml:"password"` + Host string `toml:"host"` + Port string `toml:"port"` + Database string `toml:"database"` +} + +func (d *DBConfig) OnChange() error { + setDBDefault() + return nil +} + +func setDBDefault() { + if Toml.DB.Type == "" { + Toml.DB.Type = "mysql" + } + if Toml.DB.Host == "" { + Toml.DB.Host = "127.0.0.1" + } + if Toml.DB.Port == "" { + Toml.DB.Port = "3306" + } + if Toml.DB.Database == "" { + Toml.DB.Database = "goploy" + } +} diff --git a/config/toml.go b/config/toml.go index e568d08..2382db0 100644 --- a/config/toml.go +++ b/config/toml.go @@ -6,15 +6,11 @@ package config import ( "fmt" - "github.com/pelletier/go-toml/v2" - log "github.com/sirupsen/logrus" - "io" + "github.com/knadh/koanf/parsers/toml" + "github.com/knadh/koanf/providers/file" + "github.com/knadh/koanf/providers/structs" + "github.com/knadh/koanf/v2" "os" - "path" - "path/filepath" - "runtime" - "strings" - "time" ) type Config struct { @@ -33,155 +29,85 @@ type Config struct { Cache CacheConfig `toml:"cache"` } -type APPConfig struct { - DeployLimit int32 `toml:"deployLimit"` - ShutdownTimeout time.Duration `toml:"shutdownTimeout"` - RepositoryPath string `toml:"repositoryPath"` - PasswordPeriod int `toml:"passwordPeriod"` -} - -type CORSConfig struct { - Enabled bool `toml:"enabled"` - Origins string `toml:"origins"` - Methods string `toml:"methods"` - Headers string `toml:"headers"` - Credentials bool `toml:"credentials"` -} - -type CookieConfig struct { - Name string `toml:"name"` - Expire int `toml:"expire"` // second -} - -type JWTConfig struct { - Key string `toml:"key"` -} - -type DBConfig struct { - Type string `toml:"type"` - User string `toml:"user"` - Password string `toml:"password"` - Host string `toml:"host"` - Port string `toml:"port"` - Database string `toml:"database"` -} - -type LogConfig struct { - Path string `toml:"path"` -} - -type WebConfig struct { - Port string `toml:"port"` -} - -type LDAPConfig struct { - Enabled bool `toml:"enabled"` - URL string `toml:"url"` - BindDN string `toml:"bindDN"` - Password string `toml:"password"` - BaseDN string `toml:"baseDN"` - UID string `toml:"uid"` - Name string `toml:"name"` - UserFilter string `toml:"userFilter"` -} - -type DingtalkConfig struct { - AppKey string `toml:"appKey"` - AppSecret string `toml:"appSecret"` -} - -type FeishuConfig struct { - AppKey string `toml:"appKey"` - AppSecret string `toml:"appSecret"` -} - -type CaptchaConfig struct { - Enabled bool `toml:"enabled"` -} - -type CacheConfig struct { - Type string `toml:"type"` -} - var Toml Config +var Koanf = koanf.New(".") func InitToml() { - config, err := os.ReadFile(GetConfigFile()) - if err != nil { - panic(err) - } - err = toml.Unmarshal(config, &Toml) - if err != nil { + // If first time load config error, need to panic + if err := setToml(); err != nil { panic(err) } setAPPDefault() setDBDefault() setLogger() -} -func setAPPDefault() { - if Toml.APP.ShutdownTimeout == 0 { - Toml.APP.ShutdownTimeout = 10 - } -} + GetEventBus().Subscribe(APPEventTopic, &Toml.APP) + GetEventBus().Subscribe(LogEventTopic, &Toml.Log) + GetEventBus().Subscribe(DBEventTopic, &Toml.DB) -func setDBDefault() { - if Toml.DB.Type == "" { - Toml.DB.Type = "mysql" - } - if Toml.DB.Host == "" { - Toml.DB.Host = "127.0.0.1" - } - if Toml.DB.Port == "" { - Toml.DB.Port = "3306" - } - if Toml.DB.Database == "" { - Toml.DB.Database = "goploy" - } -} - -func setLogger() { - var logFile io.Writer - logPathEnv := Toml.Log.Path - if strings.ToLower(logPathEnv) == "stdout" { - logFile = os.Stdout - } else { - logPath, err := filepath.Abs(logPathEnv) + err := file.Provider(GetConfigFile()).Watch(func(event interface{}, err error) { if err != nil { fmt.Println(err.Error()) + return } - if _, err := os.Stat(logPath); err != nil && os.IsNotExist(err) { - if err := os.Mkdir(logPath, os.ModePerm); nil != err { - panic(err.Error()) - } - } - logFile, err = os.OpenFile(logPath+"/goploy.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0766) - if nil != err { - panic(err.Error()) - } - } - log.SetReportCaller(true) - log.SetFormatter(&log.TextFormatter{ - CallerPrettyfier: func(f *runtime.Frame) (string, string) { - return fmt.Sprintf("%s()", path.Base(f.Function)), fmt.Sprintf("%s:%d", path.Base(f.File), f.Line) - }, + oldToml := Toml + + if err = setToml(); err != nil { + fmt.Println(err.Error()) + return + } + + if err = PublishEvents(Toml, getEventTopics(oldToml, Toml)); err != nil { + // If new config publish events error, use the old config + fmt.Printf("publish config events error: %v \n", err) + errToml := Toml + Toml = oldToml + _ = PublishEvents(Toml, getEventTopics(errToml, Toml)) + } }) - log.SetOutput(logFile) + if err != nil { + panic(err) + } +} - log.SetLevel(log.TraceLevel) +func getEventTopics(oldToml Config, newToml Config) (topics []string) { + if oldToml.DB != newToml.DB { + topics = append(topics, DBEventTopic) + } + if oldToml.Log != newToml.Log { + topics = append(topics, LogEventTopic) + } + + if oldToml.APP != newToml.APP { + topics = append(topics, APPEventTopic) + } + + return topics +} + +func setToml() error { + if err := Koanf.Load(file.Provider(GetConfigFile()), toml.Parser()); err != nil { + return fmt.Errorf("load config file error: %s", err) + } + + if err := Koanf.Unmarshal("", &Toml); err != nil { + return fmt.Errorf("unmarshal config error: %s", err) + } + + return nil } func Write(cfg Config) error { - yamlData, err := toml.Marshal(&cfg) - - if err != nil { + if err := Koanf.Load(structs.Provider(cfg, "toml"), nil); err != nil { return err } - err = os.WriteFile(GetConfigFile(), yamlData, 0644) + b, _ := Koanf.Marshal(toml.Parser()) + + err := os.WriteFile(GetConfigFile(), b, 0644) if err != nil { return err } diff --git a/config/web.go b/config/web.go new file mode 100644 index 0000000..38a4e3f --- /dev/null +++ b/config/web.go @@ -0,0 +1,5 @@ +package config + +type WebConfig struct { + Port string `toml:"port"` +} diff --git a/go.mod b/go.mod index 72a4652..52e829e 100644 --- a/go.mod +++ b/go.mod @@ -33,16 +33,26 @@ require ( github.com/docker/distribution v2.8.3+incompatible // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.5.0 // indirect + github.com/fatih/structs v1.1.0 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/go-asn1-ber/asn1-ber v1.5.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/klauspost/compress v1.17.3 // indirect + github.com/knadh/koanf/maps v0.1.1 // indirect + github.com/knadh/koanf/parsers/toml v0.1.0 // indirect + github.com/knadh/koanf/providers/file v0.1.0 // indirect + github.com/knadh/koanf/providers/structs v0.1.0 // indirect + github.com/knadh/koanf/v2 v2.0.1 // indirect github.com/kr/fs v0.1.0 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect github.com/leodido/go-urn v1.2.0 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/moby/patternmatcher v0.6.0 // indirect github.com/moby/sys/sequential v0.5.0 // indirect github.com/moby/term v0.5.0 // indirect @@ -50,11 +60,12 @@ require ( github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b // indirect github.com/opencontainers/runc v1.1.10 // indirect + github.com/pelletier/go-toml v1.9.5 // indirect github.com/pkg/errors v0.9.1 // indirect golang.org/x/image v0.10.0 // indirect golang.org/x/mod v0.11.0 // indirect golang.org/x/net v0.17.0 // indirect - golang.org/x/sys v0.15.0 // indirect + golang.org/x/sys v0.16.0 // indirect golang.org/x/time v0.4.0 // indirect golang.org/x/tools v0.10.0 // indirect gopkg.in/go-playground/assert.v1 v1.2.1 // indirect diff --git a/go.sum b/go.sum index 00f25c8..c61900f 100644 --- a/go.sum +++ b/go.sum @@ -25,6 +25,10 @@ github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKoh github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-asn1-ber/asn1-ber v1.5.3 h1:u7utq56RUFiynqUzgVMFDymapcOtQ/MZkh3H4QYkxag= github.com/go-asn1-ber/asn1-ber v1.5.3/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= @@ -62,6 +66,16 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.17.3 h1:qkRjuerhUU1EmXLYGkSH6EZL+vPSxIrYjLNAK4slzwA= github.com/klauspost/compress v1.17.3/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/knadh/koanf/maps v0.1.1 h1:G5TjmUh2D7G2YWf5SQQqSiHRJEjaicvU0KpypqB3NIs= +github.com/knadh/koanf/maps v0.1.1/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI= +github.com/knadh/koanf/parsers/toml v0.1.0 h1:S2hLqS4TgWZYj4/7mI5m1CQQcWurxUz6ODgOub/6LCI= +github.com/knadh/koanf/parsers/toml v0.1.0/go.mod h1:yUprhq6eo3GbyVXFFMdbfZSo928ksS+uo0FFqNMnO18= +github.com/knadh/koanf/providers/file v0.1.0 h1:fs6U7nrV58d3CFAFh8VTde8TM262ObYf3ODrc//Lp+c= +github.com/knadh/koanf/providers/file v0.1.0/go.mod h1:rjJ/nHQl64iYCtAW2QQnF0eSmDEX/YZ/eNFj5yR6BvA= +github.com/knadh/koanf/providers/structs v0.1.0 h1:wJRteCNn1qvLtE5h8KQBvLJovidSdntfdyIbbCzEyE0= +github.com/knadh/koanf/providers/structs v0.1.0/go.mod h1:sw2YZ3txUcqA3Z27gPlmmBzWn1h8Nt9O6EP/91MkcWE= +github.com/knadh/koanf/v2 v2.0.1 h1:1dYGITt1I23x8cfx8ZnldtezdyaZtfAuRtIFOiRzK7g= +github.com/knadh/koanf/v2 v2.0.1/go.mod h1:ZeiIlIDXTE7w1lMT6UVcNiRAS2/rCeLn/GdLNvY1Dus= github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= @@ -70,6 +84,12 @@ github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhR github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= @@ -84,6 +104,8 @@ github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b h1 github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= github.com/opencontainers/runc v1.1.10 h1:EaL5WeO9lv9wmS6SASjszOeQdSctvpbu0DdBQBizE40= github.com/opencontainers/runc v1.1.10/go.mod h1:+/R6+KmDlh+hOO8NkjmgkG9Qzvypzk0yXxAPYYR65+M= +github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= +github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.0.0-beta.4 h1:GCs8ebsDtEH3RiO78+BvhHqj65d/I6tjESitJZc07Rc= github.com/pelletier/go-toml/v2 v2.0.0-beta.4/go.mod h1:ke6xncR3W76Ba8xnVxkrZG0js6Rd2BsQEAYrfgJ6eQA= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -152,6 +174,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= diff --git a/internal/model/model.go b/internal/model/model.go index ea0770f..7023eb4 100644 --- a/internal/model/model.go +++ b/internal/model/model.go @@ -12,7 +12,6 @@ import ( "github.com/zhenorzz/goploy/config" "github.com/zhenorzz/goploy/database" "github.com/zhenorzz/goploy/internal/pkg" - "log" "net/url" "path" "sort" @@ -46,10 +45,26 @@ const ( DENY ) +type Model struct { + *sql.DB + config.BaseObserver +} + +func (d *Model) OnChange() error { + return connectDB() +} + // DB init when the program start -var DB *sql.DB +var DB = &Model{} func Init() { + if err := connectDB(); err != nil { + panic(err) + } + config.GetEventBus().Subscribe(config.DBEventTopic, DB) +} + +func connectDB() error { dbConn := fmt.Sprintf( "%s:%s@(%s:%s)/%s?charset=utf8mb4,utf8", config.Toml.DB.User, @@ -62,16 +77,18 @@ func Init() { { // @see https://github.com/go-sql-driver/mysql/wiki/Examples#a-word-on-sqlopen var err error - DB, err = sql.Open(config.Toml.DB.Type, dbConn) + DB.DB, err = sql.Open(config.Toml.DB.Type, dbConn) if err != nil { - log.Fatal(err) + return err } // ping db to make sure the db has connected - if err := DB.Ping(); err != nil { - log.Fatal(err) + if err = DB.Ping(); err != nil { + return err } } + + return nil } // PaginationFrom param return pagination struct @@ -106,7 +123,7 @@ func UseDB(db *sql.DB, name string) error { return nil } -func ImportSQL(db *sql.DB, sqlPath string) error { +func ImportSQL(db *Model, sqlPath string) error { sqlContent, err := database.File.ReadFile(sqlPath) if err != nil { return err