WIP: rework the demo data reading via config parameter(s)

This commit is contained in:
Sonja Happ 2021-04-29 12:45:55 +02:00
parent e8673fad31
commit b1c08f16b0
4 changed files with 266 additions and 153 deletions

View file

@ -27,6 +27,7 @@ import (
"io/ioutil" "io/ioutil"
"log" "log"
"os" "os"
"sort"
"strings" "strings"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
@ -50,11 +51,11 @@ func InitConfig() error {
dbUser = flag.String("db-user", "", "Username of database connection (default is <empty>)") dbUser = flag.String("db-user", "", "Username of database connection (default is <empty>)")
dbPass = flag.String("db-pass", "", "Password of database connection (default is <empty>)") dbPass = flag.String("db-pass", "", "Password of database connection (default is <empty>)")
dbSSLMode = flag.String("db-ssl-mode", "disable", "SSL mode of DB (default is disable)") // TODO: change default for production dbSSLMode = flag.String("db-ssl-mode", "disable", "SSL mode of DB (default is disable)") // TODO: change default for production
dbClear = flag.Bool("db-clear", false, "Set to true if you want to clear all DB tables upon startup. This parameter has to be used with great care, its effects cannot be reverted.")
amqpHost = flag.String("amqp-host", "", "If set, use this as host for AMQP broker (default is disabled)") amqpHost = flag.String("amqp-host", "", "If set, use this as host for AMQP broker (default is disabled)")
amqpUser = flag.String("amqp-user", "", "Username for AMQP broker") amqpUser = flag.String("amqp-user", "", "Username for AMQP broker")
amqpPass = flag.String("amqp-pass", "", "Password for AMQP broker") amqpPass = flag.String("amqp-pass", "", "Password for AMQP broker")
configFile = flag.String("config", "", "Path to YAML configuration file") configFile = flag.String("config", "", "Path to YAML configuration file")
mode = flag.String("mode", "release", "Select debug/release/test mode (default is release)")
port = flag.String("port", "4000", "Port of the backend (default is 4000)") port = flag.String("port", "4000", "Port of the backend (default is 4000)")
adminUser = flag.String("admin-user", "", "Initial admin username") adminUser = flag.String("admin-user", "", "Initial admin username")
adminPass = flag.String("admin-pass", "", "Initial admin password") adminPass = flag.String("admin-pass", "", "Initial admin password")
@ -74,7 +75,7 @@ func InitConfig() error {
subTitle = flag.String("sub-title", "", "Sub-title shown in the frontend") subTitle = flag.String("sub-title", "", "Sub-title shown in the frontend")
contactName = flag.String("contact-name", "Steffen Vogel", "Name of the administrative contact") contactName = flag.String("contact-name", "Steffen Vogel", "Name of the administrative contact")
contactMail = flag.String("contact-mail", "svogel2@eonerc.rwth-aachen.de", "EMail of the administrative contact") contactMail = flag.String("contact-mail", "svogel2@eonerc.rwth-aachen.de", "EMail of the administrative contact")
testDataPath = flag.String("test-data-path", "database/testdata.json", "The path to the test data json file (used in test mode)") testDataPath = flag.String("test-data-path", "", "The path to a test data json file")
groupsPath = flag.String("groups-path", "configuration/groups.yaml", "The path to a YAML file that maps user groups to scenario IDs") groupsPath = flag.String("groups-path", "configuration/groups.yaml", "The path to a YAML file that maps user groups to scenario IDs")
) )
flag.Parse() flag.Parse()
@ -88,7 +89,6 @@ func InitConfig() error {
"amqp.host": *amqpHost, "amqp.host": *amqpHost,
"amqp.user": *amqpUser, "amqp.user": *amqpUser,
"amqp.pass": *amqpPass, "amqp.pass": *amqpPass,
"mode": *mode,
"port": *port, "port": *port,
"admin.user": *adminUser, "admin.user": *adminUser,
"admin.pass": *adminPass, "admin.pass": *adminPass,
@ -107,6 +107,13 @@ func InitConfig() error {
"contact.mail": *contactMail, "contact.mail": *contactMail,
"test.datapath": *testDataPath, "test.datapath": *testDataPath,
"groups.path": *groupsPath, "groups.path": *groupsPath,
"config.file": *configFile,
}
if *dbClear == true {
static["db.clear"] = "true"
} else {
static["db.clear"] = "false"
} }
if *s3NoSSL == true { if *s3NoSSL == true {
@ -152,20 +159,20 @@ func InitConfig() error {
return err return err
} }
m, err := GlobalConfig.String("mode")
if err != nil {
return err
}
if m != "test" {
settings, _ := GlobalConfig.Settings() settings, _ := GlobalConfig.Settings()
log.Print("All settings:")
for key, val := range settings {
// TODO password settings should be excluded!
log.Printf(" %s = %s \n", key, val)
}
}
keys := make([]string, 0, len(settings))
for k := range settings {
keys = append(keys, k)
}
sort.Strings(keys)
log.Print("All settings (except for PW and secrets):")
for _, k := range keys {
if !strings.Contains(k, "pass") && !strings.Contains(k, "secret") {
log.Printf(" %s = %s \n", k, settings[k])
}
}
return nil return nil
} }

View file

@ -1,4 +1,4 @@
/** Database package. /** Package database
* *
* @author Sonja Happ <sonja.happ@eonerc.rwth-aachen.de> * @author Sonja Happ <sonja.happ@eonerc.rwth-aachen.de>
* @copyright 2014-2019, Institute for Automation of Complex Power Systems, EONERC * @copyright 2014-2019, Institute for Automation of Complex Power Systems, EONERC
@ -36,8 +36,8 @@ import (
var DBpool *gorm.DB // database used by backend var DBpool *gorm.DB // database used by backend
// Initialize connection to the database // InitDB Initialize connection to the database
func InitDB(cfg *config.Config) error { func InitDB(cfg *config.Config, dbClear string) error {
name, err := cfg.String("db.name") name, err := cfg.String("db.name")
if err != nil { if err != nil {
return err return err
@ -73,20 +73,25 @@ func InitDB(cfg *config.Config) error {
} }
DBpool = db DBpool = db
// drop tables if parameter set
if dbClear == "true" {
DropTables()
log.Println("Database tables dropped")
}
MigrateModels() MigrateModels()
log.Println("Database connection established") log.Println("Database connection established")
return nil return nil
} }
// Connection pool to already opened DB // GetDB Connection pool to already opened DB
func GetDB() *gorm.DB { func GetDB() *gorm.DB {
return DBpool return DBpool
} }
// Drop all the tables of the database // DropTables drops all the tables of the database (use with care!)
// TODO: Remove that function from the codebase and substitute the body
// to the Dummy*() where it is called
func DropTables() { func DropTables() {
DBpool.DropTableIfExists(&InfrastructureComponent{}) DBpool.DropTableIfExists(&InfrastructureComponent{})
DBpool.DropTableIfExists(&Signal{}) DBpool.DropTableIfExists(&Signal{})
@ -101,7 +106,7 @@ func DropTables() {
DBpool.DropTableIfExists("user_scenarios") DBpool.DropTableIfExists("user_scenarios")
} }
// AutoMigrate the models // MigrateModels AutoMigrate the models
func MigrateModels() { func MigrateModels() {
DBpool.AutoMigrate(&InfrastructureComponent{}) DBpool.AutoMigrate(&InfrastructureComponent{})
DBpool.AutoMigrate(&Signal{}) DBpool.AutoMigrate(&Signal{})
@ -115,26 +120,27 @@ func MigrateModels() {
} }
// DBAddAdminUser adds a default admin user to the DB // DBAddAdminUser adds a default admin user to the DB
func DBAddAdminUser(cfg *config.Config) (error, string) { func DBAddAdminUser(cfg *config.Config) error {
DBpool.AutoMigrate(User{}) DBpool.AutoMigrate(User{})
// Check if admin user exists in DB // Check if admin user exists in DB
var users []User var users []User
err := DBpool.Where("Role = ?", "Admin").Find(&users).Error err := DBpool.Where("Role = ?", "Admin").Find(&users).Error
adminPW := "" adminPW := ""
adminName := ""
if len(users) == 0 { if len(users) == 0 {
fmt.Println("No admin user found in DB, adding default admin user.") fmt.Println("No admin user found in DB, adding default admin user.")
name, err := cfg.String("admin.user") adminName, err = cfg.String("admin.user")
if err != nil || name == "" { if err != nil || adminName == "" {
name = "admin" adminName = "admin"
} }
adminPW, err = cfg.String("admin.pass") adminPW, err = cfg.String("admin.pass")
if err != nil || adminPW == "" { if err != nil || adminPW == "" {
adminPW = generatePassword(16) adminPW = generatePassword(16)
fmt.Printf(" Generated admin password: %s\n", adminPW) fmt.Printf(" Generated admin password: %s for admin user %s\n", adminPW, adminName)
} }
mail, err := cfg.String("admin.mail") mail, err := cfg.String("admin.mail")
@ -145,14 +151,16 @@ func DBAddAdminUser(cfg *config.Config) (error, string) {
pwEnc, _ := bcrypt.GenerateFromPassword([]byte(adminPW), 10) pwEnc, _ := bcrypt.GenerateFromPassword([]byte(adminPW), 10)
// create a copy of global test data // create a copy of global test data
user := User{Username: name, Password: string(pwEnc), user := User{Username: adminName, Password: string(pwEnc),
Role: "Admin", Mail: mail, Active: true} Role: "Admin", Mail: mail, Active: true}
// add admin user to DB // add admin user to DB
err = DBpool.Create(&user).Error err = DBpool.Create(&user).Error
if err != nil {
return err
} }
}
return err, adminPW return nil
} }
func generatePassword(Len int) string { func generatePassword(Len int) string {

View file

@ -102,9 +102,13 @@ func RegisterEndpoints(router *gin.Engine, api *gin.RouterGroup) {
} }
// Read test data from JSON file (path set by ENV variable or command line param) // ReadTestDataFromJson Reads test data from JSON file (path set by ENV variable or command line param)
func ReadTestDataFromJson(path string) error { func ReadTestDataFromJson(path string) error {
_, err := os.Stat(path)
if err == nil {
jsonFile, err := os.Open(path) jsonFile, err := os.Open(path)
if err != nil { if err != nil {
return fmt.Errorf("error opening json file: %v", err) return fmt.Errorf("error opening json file: %v", err)
@ -119,24 +123,34 @@ func ReadTestDataFromJson(path string) error {
if err != nil { if err != nil {
return fmt.Errorf("error unmarshalling json: %v", err) return fmt.Errorf("error unmarshalling json: %v", err)
} }
} else if os.IsNotExist(err) {
log.Println("Test data file does not exist, no test data added to DB:", path)
return nil
} else {
log.Println("Something is wrong with this file path:", path)
return nil
}
return nil return nil
} }
// Uses API endpoints to add test data to the backend; All endpoints have to be registered before invoking this function. // AddTestData Uses API endpoints to add test data to the backend; All endpoints have to be registered before invoking this function.
func AddTestData(cfg *config.Config, router *gin.Engine) (*bytes.Buffer, error) { func AddTestData(cfg *config.Config, router *gin.Engine) (*bytes.Buffer, error) {
database.MigrateModels()
// add Admin user (defaults to User_0, xyz789) adminPW, err := cfg.String("admin.pass")
err, adminpw := database.DBAddAdminUser(cfg) if err != nil {
log.Println("WARNING: cannot add test data: ", err)
var Admin = helper.Credentials{ return nil, nil
Username: "admin", }
Password: adminpw, adminName, err := cfg.String("admin.user")
if err != nil {
log.Println("WARNING: cannot add test data: ", err)
return nil, nil
} }
if err != nil { var Admin = helper.Credentials{
return nil, err Username: adminName,
Password: adminPW,
} }
// authenticate as admin // authenticate as admin
@ -147,13 +161,24 @@ func AddTestData(cfg *config.Config, router *gin.Engine) (*bytes.Buffer, error)
basePath := "/api/v2" basePath := "/api/v2"
db := database.GetDB()
// add users // add users
for _, u := range GlobalTestData.Users { for _, u := range GlobalTestData.Users {
var x []user.User
err = db.Find(&x, "Username = ?", u.Username).Error
if err != nil {
return nil, err
}
if len(x) == 0 {
code, resp, err := helper.TestEndpoint(router, token, basePath+"/users", "POST", helper.KeyModels{"user": u}) code, resp, err := helper.TestEndpoint(router, token, basePath+"/users", "POST", helper.KeyModels{"user": u})
if code != http.StatusOK { if code != http.StatusOK {
return resp, fmt.Errorf("error adding user %v: %v", u.Username, err) return resp, fmt.Errorf("error adding user %v: %v", u.Username, err)
} }
} }
}
// add infrastructure components // add infrastructure components
amqphost, _ := cfg.String("amqp.host") amqphost, _ := cfg.String("amqp.host")
@ -161,6 +186,14 @@ func AddTestData(cfg *config.Config, router *gin.Engine) (*bytes.Buffer, error)
for _, i := range GlobalTestData.ICs { for _, i := range GlobalTestData.ICs {
if (i.ManagedExternally && amqphost != "") || !i.ManagedExternally { if (i.ManagedExternally && amqphost != "") || !i.ManagedExternally {
var x []infrastructure_component.InfrastructureComponent
err = db.Find(&x, "Name = ?", i.Name).Error
if err != nil {
return nil, err
}
if len(x) == 0 {
code, resp, err := helper.TestEndpoint(router, token, basePath+"/ic", "POST", helper.KeyModels{"ic": i}) code, resp, err := helper.TestEndpoint(router, token, basePath+"/ic", "POST", helper.KeyModels{"ic": i})
if code != http.StatusOK { if code != http.StatusOK {
return resp, fmt.Errorf("error adding IC %v: %v", i.Name, err) return resp, fmt.Errorf("error adding IC %v: %v", i.Name, err)
@ -168,9 +201,18 @@ func AddTestData(cfg *config.Config, router *gin.Engine) (*bytes.Buffer, error)
counterICs++ counterICs++
} }
} }
}
// add scenarios // add scenarios
for _, s := range GlobalTestData.Scenarios { for _, s := range GlobalTestData.Scenarios {
var x []scenario.Scenario
err = db.Find(&x, "Name = ?", s.Name).Error
if err != nil {
return nil, err
}
if len(x) == 0 {
code, resp, err := helper.TestEndpoint(router, token, basePath+"/scenarios", "POST", helper.KeyModels{"scenario": s}) code, resp, err := helper.TestEndpoint(router, token, basePath+"/scenarios", "POST", helper.KeyModels{"scenario": s})
if code != http.StatusOK { if code != http.StatusOK {
return resp, fmt.Errorf("error adding Scenario %v: %v", s.Name, err) return resp, fmt.Errorf("error adding Scenario %v: %v", s.Name, err)
@ -184,18 +226,28 @@ func AddTestData(cfg *config.Config, router *gin.Engine) (*bytes.Buffer, error)
} }
} }
} }
}
// If there is at least one scenario and one IC in the test data, add component configs // If there is at least one scenario and one IC in the test data, add component configs
configCounter := 0 configCounter := 0
if len(GlobalTestData.Scenarios) > 0 && counterICs > 0 { if len(GlobalTestData.Scenarios) > 0 && counterICs > 0 {
for _, c := range GlobalTestData.Configs { for _, c := range GlobalTestData.Configs {
var x []component_configuration.ComponentConfiguration
err = db.Find(&x, "Name = ?", c.Name).Error
if err != nil {
return nil, err
}
if len(x) == 0 {
c.ScenarioID = 1 c.ScenarioID = 1
c.ICID = 1 c.ICID = 1
code, resp, err := helper.TestEndpoint(router, token, basePath+"/configs", "POST", helper.KeyModels{"config": c}) code, resp, err := helper.TestEndpoint(router, token, basePath+"/configs", "POST", helper.KeyModels{"config": c})
if code != http.StatusOK { if code != http.StatusOK {
return resp, fmt.Errorf("error adding Config %v: %v", c.Name, err) return resp, fmt.Errorf("error adding Config %v: %v", c.Name, err)
} }
}
configCounter++ configCounter++
} }
} }
@ -204,15 +256,32 @@ func AddTestData(cfg *config.Config, router *gin.Engine) (*bytes.Buffer, error)
dashboardCounter := 0 dashboardCounter := 0
if len(GlobalTestData.Scenarios) > 0 { if len(GlobalTestData.Scenarios) > 0 {
for _, d := range GlobalTestData.Dashboards { for _, d := range GlobalTestData.Dashboards {
var x []dashboard.Dashboard
err = db.Find(&x, "Name = ?", d.Name).Error
if err != nil {
return nil, err
}
if len(x) == 0 {
d.ScenarioID = 1 d.ScenarioID = 1
code, resp, err := helper.TestEndpoint(router, token, basePath+"/dashboards", "POST", helper.KeyModels{"dashboard": d}) code, resp, err := helper.TestEndpoint(router, token, basePath+"/dashboards", "POST", helper.KeyModels{"dashboard": d})
if code != http.StatusOK { if code != http.StatusOK {
return resp, fmt.Errorf("error adding Dashboard %v: %v", d.Name, err) return resp, fmt.Errorf("error adding Dashboard %v: %v", d.Name, err)
} }
}
dashboardCounter++ dashboardCounter++
} }
for _, r := range GlobalTestData.Results { for _, r := range GlobalTestData.Results {
var x []result.Result
err = db.Find(&x, "Description = ?", r.Description).Error
if err != nil {
return nil, err
}
if len(x) == 0 {
r.ScenarioID = 1 r.ScenarioID = 1
r.ResultFileIDs = []int64{} r.ResultFileIDs = []int64{}
code, resp, err := helper.TestEndpoint(router, token, basePath+"/results", "POST", helper.KeyModels{"result": r}) code, resp, err := helper.TestEndpoint(router, token, basePath+"/results", "POST", helper.KeyModels{"result": r})
@ -220,9 +289,17 @@ func AddTestData(cfg *config.Config, router *gin.Engine) (*bytes.Buffer, error)
return resp, fmt.Errorf("error adding Result %v: %v", r.Description, err) return resp, fmt.Errorf("error adding Result %v: %v", r.Description, err)
} }
} }
}
// upload files // upload files
var x []file.File
err = db.Find(&x, "Name = ?", "Readme.md").Error
if err != nil {
return nil, err
}
if len(x) == 0 {
// upload readme file // upload readme file
bodyBuf := &bytes.Buffer{} bodyBuf := &bytes.Buffer{}
bodyWriter := multipart.NewWriter(bodyBuf) bodyWriter := multipart.NewWriter(bodyBuf)
@ -241,17 +318,25 @@ func AddTestData(cfg *config.Config, router *gin.Engine) (*bytes.Buffer, error)
req1.Header.Set("Content-Type", contentType) req1.Header.Set("Content-Type", contentType)
req1.Header.Add("Authorization", "Bearer "+token) req1.Header.Add("Authorization", "Bearer "+token)
router.ServeHTTP(w1, req1) router.ServeHTTP(w1, req1)
}
var y []file.File
err = db.Find(&y, "Name = ?", "logo.png").Error
if err != nil {
return nil, err
}
if len(y) == 0 {
// upload image file // upload image file
bodyBuf = &bytes.Buffer{} bodyBuf := &bytes.Buffer{}
bodyWriter = multipart.NewWriter(bodyBuf) bodyWriter := multipart.NewWriter(bodyBuf)
fileWriter, _ = bodyWriter.CreateFormFile("file", "logo.png") fileWriter, _ := bodyWriter.CreateFormFile("file", "logo.png")
fh, _ = os.Open("doc/pictures/villas_web.png") fh, _ := os.Open("doc/pictures/villas_web.png")
defer fh.Close() defer fh.Close()
// io copy // io copy
_, err = io.Copy(fileWriter, fh) _, err = io.Copy(fileWriter, fh)
contentType = bodyWriter.FormDataContentType() contentType := bodyWriter.FormDataContentType()
bodyWriter.Close() bodyWriter.Close()
// Create the request and add a second file to scenario // Create the request and add a second file to scenario
@ -260,12 +345,20 @@ func AddTestData(cfg *config.Config, router *gin.Engine) (*bytes.Buffer, error)
req2.Header.Set("Content-Type", contentType) req2.Header.Set("Content-Type", contentType)
req2.Header.Add("Authorization", "Bearer "+token) req2.Header.Add("Authorization", "Bearer "+token)
router.ServeHTTP(w2, req2) router.ServeHTTP(w2, req2)
}
} }
// If there is at least one dashboard, add widgets // If there is at least one dashboard, add widgets
if dashboardCounter > 0 { if dashboardCounter > 0 {
for _, w := range GlobalTestData.Widgets { for _, w := range GlobalTestData.Widgets {
var x []widget.Widget
err = db.Find(&x, "Name = ?", w.Name).Error
if err != nil {
return nil, err
}
if len(x) == 0 {
w.DashboardID = 1 w.DashboardID = 1
code, resp, err := helper.TestEndpoint(router, token, basePath+"/widgets", "POST", helper.KeyModels{"widget": w}) code, resp, err := helper.TestEndpoint(router, token, basePath+"/widgets", "POST", helper.KeyModels{"widget": w})
if code != http.StatusOK { if code != http.StatusOK {
@ -273,10 +366,19 @@ func AddTestData(cfg *config.Config, router *gin.Engine) (*bytes.Buffer, error)
} }
} }
} }
}
// If there is at least one config, add signals // If there is at least one config, add signals
if configCounter > 0 { if configCounter > 0 {
for _, s := range GlobalTestData.Signals { for _, s := range GlobalTestData.Signals {
var x []signal.Signal
err = db.Find(&x, "Name = ?", s.Name).Error
if err != nil {
return nil, err
}
if len(x) == 0 {
s.ConfigID = 1 s.ConfigID = 1
code, resp, err := helper.TestEndpoint(router, token, basePath+"/signals", "POST", helper.KeyModels{"signal": s}) code, resp, err := helper.TestEndpoint(router, token, basePath+"/signals", "POST", helper.KeyModels{"signal": s})
if code != http.StatusOK { if code != http.StatusOK {
@ -284,6 +386,7 @@ func AddTestData(cfg *config.Config, router *gin.Engine) (*bytes.Buffer, error)
} }
} }
} }
}
return nil, nil return nil, nil
} }

View file

@ -35,18 +35,14 @@ import (
func addData(router *gin.Engine, cfg *config.Config) error { func addData(router *gin.Engine, cfg *config.Config) error {
if mode, err := cfg.String("mode"); err == nil && mode == "test" {
// test mode: drop all tables and add test data to DB
database.DropTables()
log.Println("Database tables dropped, using API to add test data")
testDataPath, err := cfg.String("test.datapath") testDataPath, err := cfg.String("test.datapath")
if err != nil { if err != nil {
return err return err
} }
err = routes.ReadTestDataFromJson(testDataPath) err = routes.ReadTestDataFromJson(testDataPath)
if err != nil { if err != nil {
log.Println("testdata could not be read from json") log.Println("testdata could not be read from json file")
return err return err
} }
@ -55,14 +51,7 @@ func addData(router *gin.Engine, cfg *config.Config) error {
fmt.Println("error: testdata could not be added to DB:", err.Error(), "Response body: ", resp) fmt.Println("error: testdata could not be added to DB:", err.Error(), "Response body: ", resp)
return err return err
} }
} else {
// release mode: make sure that at least one admin user exists in DB
err, _ := database.DBAddAdminUser(cfg)
if err != nil {
fmt.Println("error: adding admin user failed:", err.Error())
return err
}
}
return nil return nil
} }
@ -87,9 +76,9 @@ func main() {
log.Fatalf("Error during initialization of global configuration: %s, aborting.", err) log.Fatalf("Error during initialization of global configuration: %s, aborting.", err)
} }
mode, err := configuration.GlobalConfig.String("mode") dbClear, err := configuration.GlobalConfig.String("db.clear")
if err != nil { if err != nil {
log.Fatalf("Error reading mode from global configuration: %s, aborting.", err) log.Fatalf("Error reading db.clear parameter from global configuration: %s, aborting.", err)
} }
port, err := configuration.GlobalConfig.String("port") port, err := configuration.GlobalConfig.String("port")
@ -110,16 +99,14 @@ func main() {
} }
// Init database // Init database
err = database.InitDB(configuration.GlobalConfig) err = database.InitDB(configuration.GlobalConfig, dbClear)
if err != nil { if err != nil {
log.Fatalf("Error during initialization of database: %s, aborting.", err) log.Fatalf("Error during initialization of database: %s, aborting.", err)
} }
defer database.DBpool.Close() defer database.DBpool.Close()
// Init endpoints // Init endpoints
if mode == "release" {
gin.SetMode(gin.ReleaseMode) gin.SetMode(gin.ReleaseMode)
}
r := gin.Default() r := gin.Default()
api := r.Group("/api/v2") api := r.Group("/api/v2")
@ -139,12 +126,20 @@ func main() {
} }
} }
// Add data to DB (if any) // Make sure that at least one admin user exists in DB
err = database.DBAddAdminUser(configuration.GlobalConfig)
if err != nil {
fmt.Println("error: adding admin user failed:", err.Error())
log.Fatal(err)
}
// Add test/demo data to DB (if any)
err = addData(r, configuration.GlobalConfig) err = addData(r, configuration.GlobalConfig)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
log.Println("Running...")
// Server at port 4000 to match frontend's redirect path // Server at port 4000 to match frontend's redirect path
r.Run(":" + port) r.Run(":" + port)
} }