package main import ( "os" "strings" "time" "git.devices.local/mawas/golang-api-skeleton/lib/cache" "git.devices.local/mawas/golang-api-skeleton/lib/cmd" "git.devices.local/mawas/golang-api-skeleton/lib/common" "git.devices.local/mawas/golang-api-skeleton/lib/config" "git.devices.local/mawas/golang-api-skeleton/lib/database" "git.devices.local/mawas/golang-api-skeleton/lib/utils" "git.devices.local/mawas/golang-api-skeleton/models" "git.devices.local/mawas/golang-api-skeleton/repositories" "git.devices.local/mawas/golang-api-skeleton/services" log "github.com/sirupsen/logrus" "github.com/spf13/viper" ) // https://towardsdatascience.com/building-restful-apis-in-golang-e3fe6e3f8f95 // https://www.voile.com/voile-straps.html // FIXME how to set unique index for username to make soft deletes possible but disallow duplicates... also how to cache a username which got delete and recerated... user ID would get overwritten, maybe pre/suffix deleted username? // TODO replace uuids with ulids // TODO use badgerdb for permissions? path:[token1, token2...] // TODO add database loadbalancing feature... so allow more hosts use first as default and other ones as fallback // TODO add userid and username to repo // TODO usrid and username for cli user is hardcoded.. no need to live in database // TODO get-stats cli command... which means unix socket support to communicate with server // TODO data versioning which will be used as reference from the log, also with predictive analytics for restore which means versioning of reference objects needs to be considered // TODO request body store as reference for log // TODO because their is a badgerdb cache which tokens are existing or which userids are existing make an existence check at uuid generation just do be sure var ( Version string // allows to set version on build APPName string // allows to overwrite app name on build APPDescription string // allows to overwrite app description on build ) func main() { log.SetFormatter(&log.JSONFormatter{}) log.SetOutput(os.Stdout) hostname, err := os.Hostname() if err != nil { hostname = "unknown" } contextLogger := log.WithFields(log.Fields{ "host": hostname, "version": Version, "app": APPName, }) // cobra sets env if flag is given so that viper can pick it up cfgFile, flagCfg, err := cmd.Initialize(Version, APPName, APPDescription) if err != nil { os.Exit(1) } // config priority: flags > env > config file if err := config.Initialize(APPName, cfgFile, flagCfg); err != nil { contextLogger.WithFields(log.Fields{ "module": "config", }).Fatal(err) } // bootstrap cache c, err := cache.Bootstrap() if err != nil { contextLogger.WithFields(log.Fields{ "module": "cache", }).Fatal(err) } defer c.Close() db, err := database.Connect(database.Credentials{ Host: viper.GetString("database.host"), Port: viper.GetInt("database.port"), Dialect: viper.GetString("database.dialect"), Database: viper.GetString("database.database"), User: viper.GetString("database.user"), Password: viper.GetString("database.password"), MaxOpenConn: viper.GetInt("database.port"), MaxIdleConn: viper.GetInt("database.port"), MaxLifeTime: viper.GetInt("database.port"), Debug: true, }) if err != nil { contextLogger.WithFields(log.Fields{ "module": "database", }).Fatal(err) } db.AutoMigrate(&models.User{}) db.AutoMigrate(&models.Token{}) userRepo := repositories.NewUserRepository(db, common.GUID{}, "", nil) userService := services.NewUserService(userRepo) // Prefill cache // TODO also add deleted users to cache for old references if userData, err := userService.ReadAll(); err != nil { contextLogger.WithFields(log.Fields{ "module": "userService", }).Fatal(err) } else { for i := range userData { if err := c.Set("user:"+userData[i].ID.String(), userData[i].Username); err != nil { // userID -> username mapping contextLogger.WithFields(log.Fields{ "module": "cache", }).Fatal(err) } if err := c.Set("user:"+userData[i].Username, userData[i].ID.String()); err != nil { // username -> userID mapping contextLogger.WithFields(log.Fields{ "module": "cache", }).Fatal(err) } } } var userID common.GUID firstname := strings.ToLower(utils.RandString(utils.RandInt(5, 10))) lastname := strings.ToLower(utils.RandString(utils.RandInt(6, 14))) username := string([]byte(firstname)[0]) + lastname user1 := &models.User{ Username: username, Firstname: strings.ToUpper(string([]byte(firstname)[0])) + string([]byte(firstname)[1:]), Lastname: strings.ToUpper(string([]byte(lastname)[0])) + string([]byte(lastname)[1:]), Email: username + "@acme.inc", } if result, err := userService.Create(user1); err != nil { panic(err) } else { utils.PrettyPrintJSON(result) if err := c.Set("user:"+result.ID.String(), result.Username); err != nil { // userID -> username mapping contextLogger.WithFields(log.Fields{ "module": "cache", }).Fatal(err) } if err := c.Set("user:"+result.Username, result.ID.String()); err != nil { // username -> userID mapping contextLogger.WithFields(log.Fields{ "module": "cache", }).Fatal(err) } userID = result.ID } userRepo = repositories.NewUserRepository(db, userID, username, c) userService = services.NewUserService(userRepo) // userID db context? // k := common.UsernameContextKey("username") // ctx := context.WithValue(context.Background(), "username", username) // db = db.WithContext(ctx) // db = db.Set("username", username) // fmt.Println(db.Get("username")) // create example token tokenRepo := repositories.NewTokenRepository(db, userID, username, c) tokenService := services.NewTokenService(tokenRepo) token1 := &models.Token{ ExpiresAt: time.Now().Add(24 * time.Hour), UserID: userID, Active: true, } if result1, err := tokenService.Create(token1); err != nil { panic(err) } else { utils.PrettyPrintJSON(result1) } result1, err := tokenService.ReadAll() if err != nil { panic(err) } utils.PrettyPrintJSON(result1) result, err := userService.ReadAll() if err != nil { panic(err) } utils.PrettyPrintJSON(result) firstname2 := strings.ToLower(utils.RandString(utils.RandInt(5, 10))) lastname2 := strings.ToLower(utils.RandString(utils.RandInt(6, 14))) username2 := string([]byte(firstname2)[0]) + lastname2 user2 := &models.User{ Username: username2, Firstname: strings.ToUpper(string([]byte(firstname2)[0])) + string([]byte(firstname2)[1:]), Lastname: strings.ToUpper(string([]byte(lastname2)[0])) + string([]byte(lastname2)[1:]), Email: username2 + "@acme.inc", } if result, err := userService.Create(user2); err != nil { panic(err) } else { utils.PrettyPrintJSON(result) result3, err := userService.ReadByID(result.Username) if err != nil { panic(err) } utils.PrettyPrintJSON(result3) } // db, _ := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{}) // userRepo := user.NewRepository(db) // userService := user.NewService(userRepo) // user1 := &user.User{ // Username: "jdoe", // Firstname: "Joe", // Lastname: "Doe", // Email: "jdoe@acme.inc", // } // result, err := userService.Create(user1) // if err != nil { // panic(err) // } // fmt.Println(result) // fmt.Println("main.go --> Environment:", viper.GetString("application.environment")) // fmt.Println("main.go --> Listen:", viper.GetString("application.listenaddress")) // fmt.Println("main.go --> Database Port: ", viper.GetInt("database.port")) // fmt.Println("main.go --> Application Port:", viper.GetInt("application.port")) // contextLogger.WithFields(log.Fields{ // "module": "config", // }).Fatal("ADSADSASd") }