From 8659bd5453ebf4adf7e3d9c6224b4aabb887615a Mon Sep 17 00:00:00 2001 From: hunterlong Date: Wed, 16 Sep 2020 00:01:44 -0700 Subject: [PATCH] read_only DB env, DSN env --- CHANGELOG.md | 4 ++ database/database.go | 93 +++++++++++++++++++++++++++++++++++-- database/routines.go | 2 + frontend/src/API.js | 4 +- frontend/src/pages/Help.vue | 6 +-- handlers/setup.go | 2 +- types/configs/connection.go | 9 +++- types/configs/file.go | 6 ++- types/configs/migration.go | 2 +- types/configs/struct.go | 26 +++++------ utils/env.go | 2 + version.txt | 2 +- 12 files changed, 131 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e8eef57..3ec00745 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 0.90.68 (09-15-2020) +- Added DB_DSN env for mysql, postgres or sqlite DSN database connection string +- Added READ_ONLY env for a read only connection to the database + # 0.90.67 (09-14-2020) - Modified core settings to update config.yml on save - Modified Theme Editor to restart the HTTP router on create/delete (fixing 404's) diff --git a/database/database.go b/database/database.go index c6136cfc..79b6fc2a 100644 --- a/database/database.go +++ b/database/database.go @@ -169,10 +169,6 @@ func Available(db Database) bool { return true } -func AmountGreaterThan1000(db *gorm.DB) *gorm.DB { - return db.Where("service = ?", 1000) -} - func (it *Db) MultipleSelects(args ...string) Database { joined := strings.Join(args, ", ") return it.Select(joined) @@ -181,6 +177,7 @@ func (it *Db) MultipleSelects(args ...string) Database { type Db struct { Database *gorm.DB Type string + ReadOnly bool } // Openw is a drop-in replacement for Open() @@ -223,6 +220,9 @@ func OpenTester() (Database, error) { default: dbString = fmt.Sprintf("file:%s?mode=memory&cache=shared", utils.RandomString(12)) } + if utils.Params.IsSet("DB_DSN") { + dbString = utils.Params.GetString("DB_DSN") + } newDb, err := Openw(testDB, dbString) if err != nil { return nil, err @@ -239,6 +239,7 @@ func Wrap(db *gorm.DB) Database { return &Db{ Database: db, Type: db.Dialect().GetName(), + ReadOnly: utils.Params.GetBool("READ_ONLY"), } } @@ -379,14 +380,26 @@ func (it *Db) Related(value interface{}, foreignKeys ...string) Database { } func (it *Db) FirstOrInit(out interface{}, where ...interface{}) Database { + if it.ReadOnly { + it.Database.Error = nil + return Wrap(it.Database) + } return Wrap(it.Database.FirstOrInit(out, where...)) } func (it *Db) FirstOrCreate(out interface{}, where ...interface{}) Database { + if it.ReadOnly { + it.Database.Error = nil + return Wrap(it.Database) + } return Wrap(it.Database.FirstOrCreate(out, where...)) } func (it *Db) Update(attrs ...interface{}) Database { + if it.ReadOnly { + it.Database.Error = nil + return Wrap(it.Database) + } return Wrap(it.Database.Update(attrs...)) } @@ -395,22 +408,42 @@ func (it *Db) Updates(values interface{}, ignoreProtectedAttrs ...bool) Database } func (it *Db) UpdateColumn(attrs ...interface{}) Database { + if it.ReadOnly { + it.Database.Error = nil + return Wrap(it.Database) + } return Wrap(it.Database.UpdateColumn(attrs...)) } func (it *Db) UpdateColumns(values interface{}) Database { + if it.ReadOnly { + it.Database.Error = nil + return Wrap(it.Database) + } return Wrap(it.Database.UpdateColumns(values)) } func (it *Db) Save(value interface{}) Database { + if it.ReadOnly { + it.Database.Error = nil + return Wrap(it.Database) + } return Wrap(it.Database.Save(value)) } func (it *Db) Create(value interface{}) Database { + if it.ReadOnly { + it.Database.Error = nil + return Wrap(it.Database) + } return Wrap(it.Database.Create(value)) } func (it *Db) Delete(value interface{}, where ...interface{}) Database { + if it.ReadOnly { + it.Database.Error = nil + return Wrap(it.Database) + } return Wrap(it.Database.Delete(value, where...)) } @@ -435,14 +468,26 @@ func (it *Db) Debug() Database { } func (it *Db) Begin() Database { + if it.ReadOnly { + it.Database.Error = nil + return Wrap(it.Database) + } return Wrap(it.Database.Begin()) } func (it *Db) Commit() Database { + if it.ReadOnly { + it.Database.Error = nil + return Wrap(it.Database) + } return Wrap(it.Database.Commit()) } func (it *Db) Rollback() Database { + if it.ReadOnly { + it.Database.Error = nil + return Wrap(it.Database) + } return Wrap(it.Database.Rollback()) } @@ -455,14 +500,26 @@ func (it *Db) RecordNotFound() bool { } func (it *Db) CreateTable(values ...interface{}) Database { + if it.ReadOnly { + it.Database.Error = nil + return Wrap(it.Database) + } return Wrap(it.Database.CreateTable(values...)) } func (it *Db) DropTable(values ...interface{}) Database { + if it.ReadOnly { + it.Database.Error = nil + return Wrap(it.Database) + } return Wrap(it.Database.DropTable(values...)) } func (it *Db) DropTableIfExists(values ...interface{}) Database { + if it.ReadOnly { + it.Database.Error = nil + return Wrap(it.Database) + } return Wrap(it.Database.DropTableIfExists(values...)) } @@ -471,26 +528,50 @@ func (it *Db) HasTable(value interface{}) bool { } func (it *Db) AutoMigrate(values ...interface{}) Database { + if it.ReadOnly { + it.Database.Error = nil + return Wrap(it.Database) + } return Wrap(it.Database.AutoMigrate(values...)) } func (it *Db) ModifyColumn(column string, typ string) Database { + if it.ReadOnly { + it.Database.Error = nil + return Wrap(it.Database) + } return Wrap(it.Database.ModifyColumn(column, typ)) } func (it *Db) DropColumn(column string) Database { + if it.ReadOnly { + it.Database.Error = nil + return Wrap(it.Database) + } return Wrap(it.Database.DropColumn(column)) } func (it *Db) AddIndex(indexName string, columns ...string) Database { + if it.ReadOnly { + it.Database.Error = nil + return Wrap(it.Database) + } return Wrap(it.Database.AddIndex(indexName, columns...)) } func (it *Db) AddUniqueIndex(indexName string, columns ...string) Database { + if it.ReadOnly { + it.Database.Error = nil + return Wrap(it.Database) + } return Wrap(it.Database.AddUniqueIndex(indexName, columns...)) } func (it *Db) RemoveIndex(indexName string) Database { + if it.ReadOnly { + it.Database.Error = nil + return Wrap(it.Database) + } return Wrap(it.Database.RemoveIndex(indexName)) } @@ -519,6 +600,10 @@ func (it *Db) SetJoinTableHandler(source interface{}, column string, handler gor } func (it *Db) AddForeignKey(field string, dest string, onDelete string, onUpdate string) Database { + if it.ReadOnly { + it.Database.Error = nil + return Wrap(it.Database) + } return Wrap(it.Database.AddForeignKey(field, dest, onDelete, onUpdate)) } diff --git a/database/routines.go b/database/routines.go index 92583e64..591277da 100644 --- a/database/routines.go +++ b/database/routines.go @@ -16,6 +16,8 @@ var ( // Maintenance will automatically delete old records from 'failures' and 'hits' // this function is currently set to delete records 7+ days old every 60 minutes +// env: REMOVE_AFTER - golang duration parsed time for deleting records older than REMOVE_AFTER duration from now +// env: CLEANUP_INTERVAL - golang duration parsed time for checking old records routine func Maintenance() { dur := utils.Params.GetDuration("REMOVE_AFTER") interval := utils.Params.GetDuration("CLEANUP_INTERVAL") diff --git a/frontend/src/API.js b/frontend/src/API.js index 8a0d3fcc..e027fcb4 100644 --- a/frontend/src/API.js +++ b/frontend/src/API.js @@ -7,8 +7,8 @@ const tokenKey = "statping_auth"; class Api { constructor() { - this.version = "0.90.66"; - this.commit = "a3ce4124f654b13c7f2af88b8410f998bd57fcef"; + this.version = "0.90.67"; + this.commit = "7e121335791d2143a2eefd404dbcce83b8f46f61"; } async oauth() { diff --git a/frontend/src/pages/Help.vue b/frontend/src/pages/Help.vue index 55639c5b..61654a40 100755 --- a/frontend/src/pages/Help.vue +++ b/frontend/src/pages/Help.vue @@ -1545,8 +1545,8 @@ systemctl start statping

You can even run Statping on your Raspberry Pi by installing the precompiled binary from Latest Releases. For the Raspberry Pi 3 you’ll want to download the statping-linux-arm7.tar.gz file. Be sure to change VERSION to the latest version in Releases, and include the ‘v’.

VERSION=$(curl -sL "https://github.com/statping/statping/releases/latest" | grep -o 'tag/[v.0-9]*' | awk -F/ '{print $2}' | head -n1)
-wget https://github.com/statping/statping/releases/download/$VERSION/statping-linux-arm7.tar.gz
-tar -xvzf statping-linux-arm7.tar.gz
+wget https://github.com/statping/statping/releases/download/$VERSION/statping-linux-arm-7.tar.gz
+tar -xvzf statping-linux-arm-7.tar.gz
 chmod +x statping
 mv statping /usr/local/bin/statping
 
@@ -2275,7 +2275,7 @@ OluFxewsEO0QNDrfFb+0gnjYlnGqOFcZjUMXbDdY5oLSPtXohynuTK1qyQ==
 
 
 
-Automatically generated from Statping's Wiki on 2020-09-09 01:24:21.649582 +0000 UTC +Automatically generated from Statping's Wiki on 2020-09-15 19:09:14.703237 +0000 UTC
diff --git a/handlers/setup.go b/handlers/setup.go index 17148b06..4c663eb2 100644 --- a/handlers/setup.go +++ b/handlers/setup.go @@ -59,7 +59,7 @@ func processSetupHandler(w http.ResponseWriter, r *http.Request) { return } - if err := configs.CreateAdminUser(confgs); err != nil { + if err := configs.CreateAdminUser(); err != nil { sendErrorJson(err, w, r) return } diff --git a/types/configs/connection.go b/types/configs/connection.go index c2fb2d96..1067b068 100644 --- a/types/configs/connection.go +++ b/types/configs/connection.go @@ -20,6 +20,7 @@ import ( "time" ) +// initModels sets the database for each Statping type packages func initModels(db database.Database) { core.SetDB(db) services.SetDB(db) @@ -67,6 +68,10 @@ func Connect(configs *DbConfig, retry bool) error { log.Infoln(fmt.Sprintf("Database %s connection was successful.", configs.DbConn)) } + if utils.Params.GetBool("READ_ONLY") { + log.Warnln("Running in READ ONLY MODE") + } + configs.Db = dbSession initModels(configs.Db) @@ -74,7 +79,9 @@ func Connect(configs *DbConfig, retry bool) error { return err } -func CreateAdminUser(c *DbConfig) error { +// CreateAdminUser will create the default admin user "admin", "admin", or use the +// environment variables ADMIN_USER, ADMIN_PASSWORD, and ADMIN_EMAIL if set. +func CreateAdminUser() error { adminUser := utils.Params.GetString("ADMIN_USER") adminPass := utils.Params.GetString("ADMIN_PASSWORD") adminEmail := utils.Params.GetString("ADMIN_EMAIL") diff --git a/types/configs/file.go b/types/configs/file.go index 9c71e075..87ff21e6 100644 --- a/types/configs/file.go +++ b/types/configs/file.go @@ -8,8 +8,9 @@ import ( "strings" ) -var log = utils.Log +var log = utils.Log.WithField("type", "configs") +// ConnectConfigs will connect to the database and save the config.yml file func ConnectConfigs(configs *DbConfig, retry bool) error { err := Connect(configs, retry) if err != nil { @@ -21,6 +22,8 @@ func ConnectConfigs(configs *DbConfig, retry bool) error { return nil } +// findDbFile will attempt to find the "statping.db" database file in the current +// working directory, or from STATPING_DIR env. func findDbFile(configs *DbConfig) (string, error) { location := utils.Directory + "/" + SqliteFilename if configs == nil { @@ -37,6 +40,7 @@ func findDbFile(configs *DbConfig) (string, error) { return location, nil } +// findSQLin walks the current walking directory for statping.db func findSQLin(path string) (string, error) { filename := SqliteFilename var found []string diff --git a/types/configs/migration.go b/types/configs/migration.go index 976639b3..1a3704f4 100644 --- a/types/configs/migration.go +++ b/types/configs/migration.go @@ -38,7 +38,7 @@ func (d *DbConfig) ResetCore() error { if err := d.CreateDatabase(); err != nil { return errors.Wrap(err, "error creating database") } - if err := CreateAdminUser(d); err != nil { + if err := CreateAdminUser(); err != nil { return errors.Wrap(err, "error creating default admin user") } if utils.Params.GetBool("SAMPLE_DATA") { diff --git a/types/configs/struct.go b/types/configs/struct.go index 93ec5ef6..c0f4741a 100644 --- a/types/configs/struct.go +++ b/types/configs/struct.go @@ -7,11 +7,11 @@ const SqliteFilename = "statping.db" // DbConfig struct is used for the Db connection and creates the 'config.yml' file type DbConfig struct { DbConn string `yaml:"connection" json:"connection"` - DbHost string `yaml:"host" json:"-"` - DbUser string `yaml:"user" json:"-"` - DbPass string `yaml:"password" json:"-"` - DbData string `yaml:"database" json:"-"` - DbPort int `yaml:"port" json:"-"` + DbHost string `yaml:"host,omitempty" json:"-"` + DbUser string `yaml:"user,omitempty" json:"-"` + DbPass string `yaml:"password,omitempty" json:"-"` + DbData string `yaml:"database,omitempty" json:"-"` + DbPort int `yaml:"port,omitempty" json:"-"` ApiSecret string `yaml:"api_secret" json:"-"` Language string `yaml:"language" json:"language"` AllowReports bool `yaml:"allow_reports" json:"allow_reports"` @@ -33,21 +33,21 @@ type DbConfig struct { DemoMode bool `yaml:"demo_mode" json:"demo_mode"` DisableLogs bool `yaml:"disable_logs" json:"disable_logs"` UseAssets bool `yaml:"use_assets" json:"use_assets"` - BasePath string `yaml:"base_path" json:"base_path"` + BasePath string `yaml:"base_path,omitempty" json:"base_path"` - AdminUser string `yaml:"admin_user" json:"admin_user"` - AdminPassword string `yaml:"admin_password" json:"admin_password"` - AdminEmail string `yaml:"admin_email" json:"admin_email"` + AdminUser string `yaml:"admin_user,omitempty" json:"admin_user"` + AdminPassword string `yaml:"admin_password,omitempty" json:"admin_password"` + AdminEmail string `yaml:"admin_email,omitempty" json:"admin_email"` - MaxOpenConnections int `yaml:"db_open_connections" json:"db_open_connections"` - MaxIdleConnections int `yaml:"db_idle_connections" json:"db_idle_connections"` - MaxLifeConnections int `yaml:"db_max_life_connections" json:"db_max_life_connections"` + MaxOpenConnections int `yaml:"db_open_connections,omitempty" json:"db_open_connections"` + MaxIdleConnections int `yaml:"db_idle_connections,omitempty" json:"db_idle_connections"` + MaxLifeConnections int `yaml:"db_max_life_connections,omitempty" json:"db_max_life_connections"` SampleData bool `yaml:"sample_data" json:"sample_data"` UseCDN bool `yaml:"use_cdn" json:"use_cdn"` DisableColors bool `yaml:"disable_colors" json:"disable_colors"` - PostgresSSLMode string `yaml:"postgres_ssl" json:"postgres_ssl"` + PostgresSSLMode string `yaml:"postgres_ssl,omitempty" json:"postgres_ssl"` Db database.Database `yaml:"-" json:"-"` } diff --git a/utils/env.go b/utils/env.go index d823fba0..a7bf9c12 100644 --- a/utils/env.go +++ b/utils/env.go @@ -30,6 +30,7 @@ func InitEnvs() { Params.SetDefault("DEBUG", false) Params.SetDefault("DEMO_MODE", false) Params.SetDefault("DB_CONN", "") + Params.SetDefault("DB_DSN", "") Params.SetDefault("DISABLE_LOGS", false) Params.SetDefault("USE_ASSETS", false) Params.SetDefault("BASE_PATH", "") @@ -53,6 +54,7 @@ func InitEnvs() { Params.SetDefault("LETSENCRYPT_EMAIL", "") Params.SetDefault("LETSENCRYPT_LOCAL", false) Params.SetDefault("LETSENCRYPT_ENABLE", false) + Params.SetDefault("READ_ONLY", false) Params.SetDefault("LOGS_MAX_COUNT", 5) Params.SetDefault("LOGS_MAX_AGE", 28) Params.SetDefault("LOGS_MAX_SIZE", 16) diff --git a/version.txt b/version.txt index b102d03f..95684375 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.90.67 \ No newline at end of file +0.90.68