read_only DB env, DSN env

pull/821/head
hunterlong 2020-09-16 00:01:44 -07:00
parent 7e12133579
commit 8659bd5453
12 changed files with 131 additions and 27 deletions

View File

@ -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) # 0.90.67 (09-14-2020)
- Modified core settings to update config.yml on save - Modified core settings to update config.yml on save
- Modified Theme Editor to restart the HTTP router on create/delete (fixing 404's) - Modified Theme Editor to restart the HTTP router on create/delete (fixing 404's)

View File

@ -169,10 +169,6 @@ func Available(db Database) bool {
return true return true
} }
func AmountGreaterThan1000(db *gorm.DB) *gorm.DB {
return db.Where("service = ?", 1000)
}
func (it *Db) MultipleSelects(args ...string) Database { func (it *Db) MultipleSelects(args ...string) Database {
joined := strings.Join(args, ", ") joined := strings.Join(args, ", ")
return it.Select(joined) return it.Select(joined)
@ -181,6 +177,7 @@ func (it *Db) MultipleSelects(args ...string) Database {
type Db struct { type Db struct {
Database *gorm.DB Database *gorm.DB
Type string Type string
ReadOnly bool
} }
// Openw is a drop-in replacement for Open() // Openw is a drop-in replacement for Open()
@ -223,6 +220,9 @@ func OpenTester() (Database, error) {
default: default:
dbString = fmt.Sprintf("file:%s?mode=memory&cache=shared", utils.RandomString(12)) 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) newDb, err := Openw(testDB, dbString)
if err != nil { if err != nil {
return nil, err return nil, err
@ -239,6 +239,7 @@ func Wrap(db *gorm.DB) Database {
return &Db{ return &Db{
Database: db, Database: db,
Type: db.Dialect().GetName(), 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 { 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...)) return Wrap(it.Database.FirstOrInit(out, where...))
} }
func (it *Db) FirstOrCreate(out interface{}, where ...interface{}) Database { 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...)) return Wrap(it.Database.FirstOrCreate(out, where...))
} }
func (it *Db) Update(attrs ...interface{}) Database { func (it *Db) Update(attrs ...interface{}) Database {
if it.ReadOnly {
it.Database.Error = nil
return Wrap(it.Database)
}
return Wrap(it.Database.Update(attrs...)) 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 { func (it *Db) UpdateColumn(attrs ...interface{}) Database {
if it.ReadOnly {
it.Database.Error = nil
return Wrap(it.Database)
}
return Wrap(it.Database.UpdateColumn(attrs...)) return Wrap(it.Database.UpdateColumn(attrs...))
} }
func (it *Db) UpdateColumns(values interface{}) Database { func (it *Db) UpdateColumns(values interface{}) Database {
if it.ReadOnly {
it.Database.Error = nil
return Wrap(it.Database)
}
return Wrap(it.Database.UpdateColumns(values)) return Wrap(it.Database.UpdateColumns(values))
} }
func (it *Db) Save(value interface{}) Database { func (it *Db) Save(value interface{}) Database {
if it.ReadOnly {
it.Database.Error = nil
return Wrap(it.Database)
}
return Wrap(it.Database.Save(value)) return Wrap(it.Database.Save(value))
} }
func (it *Db) Create(value interface{}) Database { func (it *Db) Create(value interface{}) Database {
if it.ReadOnly {
it.Database.Error = nil
return Wrap(it.Database)
}
return Wrap(it.Database.Create(value)) return Wrap(it.Database.Create(value))
} }
func (it *Db) Delete(value interface{}, where ...interface{}) Database { 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...)) return Wrap(it.Database.Delete(value, where...))
} }
@ -435,14 +468,26 @@ func (it *Db) Debug() Database {
} }
func (it *Db) Begin() Database { func (it *Db) Begin() Database {
if it.ReadOnly {
it.Database.Error = nil
return Wrap(it.Database)
}
return Wrap(it.Database.Begin()) return Wrap(it.Database.Begin())
} }
func (it *Db) Commit() Database { func (it *Db) Commit() Database {
if it.ReadOnly {
it.Database.Error = nil
return Wrap(it.Database)
}
return Wrap(it.Database.Commit()) return Wrap(it.Database.Commit())
} }
func (it *Db) Rollback() Database { func (it *Db) Rollback() Database {
if it.ReadOnly {
it.Database.Error = nil
return Wrap(it.Database)
}
return Wrap(it.Database.Rollback()) return Wrap(it.Database.Rollback())
} }
@ -455,14 +500,26 @@ func (it *Db) RecordNotFound() bool {
} }
func (it *Db) CreateTable(values ...interface{}) Database { func (it *Db) CreateTable(values ...interface{}) Database {
if it.ReadOnly {
it.Database.Error = nil
return Wrap(it.Database)
}
return Wrap(it.Database.CreateTable(values...)) return Wrap(it.Database.CreateTable(values...))
} }
func (it *Db) DropTable(values ...interface{}) Database { func (it *Db) DropTable(values ...interface{}) Database {
if it.ReadOnly {
it.Database.Error = nil
return Wrap(it.Database)
}
return Wrap(it.Database.DropTable(values...)) return Wrap(it.Database.DropTable(values...))
} }
func (it *Db) DropTableIfExists(values ...interface{}) Database { func (it *Db) DropTableIfExists(values ...interface{}) Database {
if it.ReadOnly {
it.Database.Error = nil
return Wrap(it.Database)
}
return Wrap(it.Database.DropTableIfExists(values...)) return Wrap(it.Database.DropTableIfExists(values...))
} }
@ -471,26 +528,50 @@ func (it *Db) HasTable(value interface{}) bool {
} }
func (it *Db) AutoMigrate(values ...interface{}) Database { func (it *Db) AutoMigrate(values ...interface{}) Database {
if it.ReadOnly {
it.Database.Error = nil
return Wrap(it.Database)
}
return Wrap(it.Database.AutoMigrate(values...)) return Wrap(it.Database.AutoMigrate(values...))
} }
func (it *Db) ModifyColumn(column string, typ string) Database { 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)) return Wrap(it.Database.ModifyColumn(column, typ))
} }
func (it *Db) DropColumn(column string) Database { func (it *Db) DropColumn(column string) Database {
if it.ReadOnly {
it.Database.Error = nil
return Wrap(it.Database)
}
return Wrap(it.Database.DropColumn(column)) return Wrap(it.Database.DropColumn(column))
} }
func (it *Db) AddIndex(indexName string, columns ...string) Database { 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...)) return Wrap(it.Database.AddIndex(indexName, columns...))
} }
func (it *Db) AddUniqueIndex(indexName string, columns ...string) Database { 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...)) return Wrap(it.Database.AddUniqueIndex(indexName, columns...))
} }
func (it *Db) RemoveIndex(indexName string) Database { func (it *Db) RemoveIndex(indexName string) Database {
if it.ReadOnly {
it.Database.Error = nil
return Wrap(it.Database)
}
return Wrap(it.Database.RemoveIndex(indexName)) 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 { 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)) return Wrap(it.Database.AddForeignKey(field, dest, onDelete, onUpdate))
} }

View File

@ -16,6 +16,8 @@ var (
// Maintenance will automatically delete old records from 'failures' and 'hits' // Maintenance will automatically delete old records from 'failures' and 'hits'
// this function is currently set to delete records 7+ days old every 60 minutes // 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() { func Maintenance() {
dur := utils.Params.GetDuration("REMOVE_AFTER") dur := utils.Params.GetDuration("REMOVE_AFTER")
interval := utils.Params.GetDuration("CLEANUP_INTERVAL") interval := utils.Params.GetDuration("CLEANUP_INTERVAL")

View File

@ -7,8 +7,8 @@ const tokenKey = "statping_auth";
class Api { class Api {
constructor() { constructor() {
this.version = "0.90.66"; this.version = "0.90.67";
this.commit = "a3ce4124f654b13c7f2af88b8410f998bd57fcef"; this.commit = "7e121335791d2143a2eefd404dbcce83b8f46f61";
} }
async oauth() { async oauth() {

View File

@ -1545,8 +1545,8 @@ systemctl start statping
<p>You can even run Statping on your Raspberry Pi by installing the precompiled binary from <a href="https://github.com/statping/statping/releases/latest" target="_blank">Latest Releases</a>. For the Raspberry Pi 3 you&rsquo;ll want to download the <code>statping-linux-arm7.tar.gz</code> file. Be sure to change <code>VERSION</code> to the latest version in Releases, and include the &lsquo;v&rsquo;.</p> <p>You can even run Statping on your Raspberry Pi by installing the precompiled binary from <a href="https://github.com/statping/statping/releases/latest" target="_blank">Latest Releases</a>. For the Raspberry Pi 3 you&rsquo;ll want to download the <code>statping-linux-arm7.tar.gz</code> file. Be sure to change <code>VERSION</code> to the latest version in Releases, and include the &lsquo;v&rsquo;.</p>
<pre><code>VERSION=$(curl -sL &quot;https://github.com/statping/statping/releases/latest&quot; | grep -o 'tag/[v.0-9]*' | awk -F/ '{print $2}' | head -n1) <pre><code>VERSION=$(curl -sL &quot;https://github.com/statping/statping/releases/latest&quot; | 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 wget https://github.com/statping/statping/releases/download/$VERSION/statping-linux-arm-7.tar.gz
tar -xvzf statping-linux-arm7.tar.gz tar -xvzf statping-linux-arm-7.tar.gz
chmod +x statping chmod +x statping
mv statping /usr/local/bin/statping mv statping /usr/local/bin/statping
@ -2275,7 +2275,7 @@ OluFxewsEO0QNDrfFb+0gnjYlnGqOFcZjUMXbDdY5oLSPtXohynuTK1qyQ==
</div> </div>
<div class="text-center small text-dim" v-pre> <div class="text-center small text-dim" v-pre>
Automatically generated from Statping's Wiki on 2020-09-09 01:24:21.649582 &#43;0000 UTC Automatically generated from Statping's Wiki on 2020-09-15 19:09:14.703237 &#43;0000 UTC
</div> </div>
</div> </div>

View File

@ -59,7 +59,7 @@ func processSetupHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
if err := configs.CreateAdminUser(confgs); err != nil { if err := configs.CreateAdminUser(); err != nil {
sendErrorJson(err, w, r) sendErrorJson(err, w, r)
return return
} }

View File

@ -20,6 +20,7 @@ import (
"time" "time"
) )
// initModels sets the database for each Statping type packages
func initModels(db database.Database) { func initModels(db database.Database) {
core.SetDB(db) core.SetDB(db)
services.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)) 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 configs.Db = dbSession
initModels(configs.Db) initModels(configs.Db)
@ -74,7 +79,9 @@ func Connect(configs *DbConfig, retry bool) error {
return err 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") adminUser := utils.Params.GetString("ADMIN_USER")
adminPass := utils.Params.GetString("ADMIN_PASSWORD") adminPass := utils.Params.GetString("ADMIN_PASSWORD")
adminEmail := utils.Params.GetString("ADMIN_EMAIL") adminEmail := utils.Params.GetString("ADMIN_EMAIL")

View File

@ -8,8 +8,9 @@ import (
"strings" "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 { func ConnectConfigs(configs *DbConfig, retry bool) error {
err := Connect(configs, retry) err := Connect(configs, retry)
if err != nil { if err != nil {
@ -21,6 +22,8 @@ func ConnectConfigs(configs *DbConfig, retry bool) error {
return nil 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) { func findDbFile(configs *DbConfig) (string, error) {
location := utils.Directory + "/" + SqliteFilename location := utils.Directory + "/" + SqliteFilename
if configs == nil { if configs == nil {
@ -37,6 +40,7 @@ func findDbFile(configs *DbConfig) (string, error) {
return location, nil return location, nil
} }
// findSQLin walks the current walking directory for statping.db
func findSQLin(path string) (string, error) { func findSQLin(path string) (string, error) {
filename := SqliteFilename filename := SqliteFilename
var found []string var found []string

View File

@ -38,7 +38,7 @@ func (d *DbConfig) ResetCore() error {
if err := d.CreateDatabase(); err != nil { if err := d.CreateDatabase(); err != nil {
return errors.Wrap(err, "error creating database") 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") return errors.Wrap(err, "error creating default admin user")
} }
if utils.Params.GetBool("SAMPLE_DATA") { if utils.Params.GetBool("SAMPLE_DATA") {

View File

@ -7,11 +7,11 @@ const SqliteFilename = "statping.db"
// DbConfig struct is used for the Db connection and creates the 'config.yml' file // DbConfig struct is used for the Db connection and creates the 'config.yml' file
type DbConfig struct { type DbConfig struct {
DbConn string `yaml:"connection" json:"connection"` DbConn string `yaml:"connection" json:"connection"`
DbHost string `yaml:"host" json:"-"` DbHost string `yaml:"host,omitempty" json:"-"`
DbUser string `yaml:"user" json:"-"` DbUser string `yaml:"user,omitempty" json:"-"`
DbPass string `yaml:"password" json:"-"` DbPass string `yaml:"password,omitempty" json:"-"`
DbData string `yaml:"database" json:"-"` DbData string `yaml:"database,omitempty" json:"-"`
DbPort int `yaml:"port" json:"-"` DbPort int `yaml:"port,omitempty" json:"-"`
ApiSecret string `yaml:"api_secret" json:"-"` ApiSecret string `yaml:"api_secret" json:"-"`
Language string `yaml:"language" json:"language"` Language string `yaml:"language" json:"language"`
AllowReports bool `yaml:"allow_reports" json:"allow_reports"` AllowReports bool `yaml:"allow_reports" json:"allow_reports"`
@ -33,21 +33,21 @@ type DbConfig struct {
DemoMode bool `yaml:"demo_mode" json:"demo_mode"` DemoMode bool `yaml:"demo_mode" json:"demo_mode"`
DisableLogs bool `yaml:"disable_logs" json:"disable_logs"` DisableLogs bool `yaml:"disable_logs" json:"disable_logs"`
UseAssets bool `yaml:"use_assets" json:"use_assets"` 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"` AdminUser string `yaml:"admin_user,omitempty" json:"admin_user"`
AdminPassword string `yaml:"admin_password" json:"admin_password"` AdminPassword string `yaml:"admin_password,omitempty" json:"admin_password"`
AdminEmail string `yaml:"admin_email" json:"admin_email"` AdminEmail string `yaml:"admin_email,omitempty" json:"admin_email"`
MaxOpenConnections int `yaml:"db_open_connections" json:"db_open_connections"` MaxOpenConnections int `yaml:"db_open_connections,omitempty" json:"db_open_connections"`
MaxIdleConnections int `yaml:"db_idle_connections" json:"db_idle_connections"` MaxIdleConnections int `yaml:"db_idle_connections,omitempty" json:"db_idle_connections"`
MaxLifeConnections int `yaml:"db_max_life_connections" json:"db_max_life_connections"` MaxLifeConnections int `yaml:"db_max_life_connections,omitempty" json:"db_max_life_connections"`
SampleData bool `yaml:"sample_data" json:"sample_data"` SampleData bool `yaml:"sample_data" json:"sample_data"`
UseCDN bool `yaml:"use_cdn" json:"use_cdn"` UseCDN bool `yaml:"use_cdn" json:"use_cdn"`
DisableColors bool `yaml:"disable_colors" json:"disable_colors"` 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:"-"` Db database.Database `yaml:"-" json:"-"`
} }

View File

@ -30,6 +30,7 @@ func InitEnvs() {
Params.SetDefault("DEBUG", false) Params.SetDefault("DEBUG", false)
Params.SetDefault("DEMO_MODE", false) Params.SetDefault("DEMO_MODE", false)
Params.SetDefault("DB_CONN", "") Params.SetDefault("DB_CONN", "")
Params.SetDefault("DB_DSN", "")
Params.SetDefault("DISABLE_LOGS", false) Params.SetDefault("DISABLE_LOGS", false)
Params.SetDefault("USE_ASSETS", false) Params.SetDefault("USE_ASSETS", false)
Params.SetDefault("BASE_PATH", "") Params.SetDefault("BASE_PATH", "")
@ -53,6 +54,7 @@ func InitEnvs() {
Params.SetDefault("LETSENCRYPT_EMAIL", "") Params.SetDefault("LETSENCRYPT_EMAIL", "")
Params.SetDefault("LETSENCRYPT_LOCAL", false) Params.SetDefault("LETSENCRYPT_LOCAL", false)
Params.SetDefault("LETSENCRYPT_ENABLE", false) Params.SetDefault("LETSENCRYPT_ENABLE", false)
Params.SetDefault("READ_ONLY", false)
Params.SetDefault("LOGS_MAX_COUNT", 5) Params.SetDefault("LOGS_MAX_COUNT", 5)
Params.SetDefault("LOGS_MAX_AGE", 28) Params.SetDefault("LOGS_MAX_AGE", 28)
Params.SetDefault("LOGS_MAX_SIZE", 16) Params.SetDefault("LOGS_MAX_SIZE", 16)

View File

@ -1 +1 @@
0.90.67 0.90.68