diff --git a/app/controllers/base_controller.go b/app/controllers/base_controller.go index f12fa02..6ce1239 100644 --- a/app/controllers/base_controller.go +++ b/app/controllers/base_controller.go @@ -4,12 +4,11 @@ import ( "fmt" "github.com/gorilla/mux" "github.com/gorilla/sessions" + "golang.org/x/crypto/bcrypt" "gorm.io/driver/postgres" - "moretcgshop/app/models" - "os" - "gorm.io/gorm" "log" + "moretcgshop/app/models" "net/http" ) @@ -35,16 +34,49 @@ type DBConfig struct { DBDriver string } -var store = sessions.NewCookieStore([]byte(os.Getenv("SESSION_KEY"))) +var store = sessions.NewCookieStore([]byte("DamianFinja")) //os.Getenv("SESSION_KEY"))) var sessionShoppingCart = "shopping-cart-session" var sessionFlash = "flash-session" var sessionUser = "user-session" func (server *Server) Initialize(appConfig AppConfig, dbConfig DBConfig) { fmt.Println("Willkommen zu " + appConfig.AppName) - //server.initializeDB(dbConfig) + server.initializeDB(dbConfig) server.initializeAppConfig(appConfig) - server.initializeRoutes() + server.routeInit() +} + +func SetFlash(w http.ResponseWriter, r *http.Request, name string, value string) { + session, err := store.Get(r, sessionFlash) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + session.AddFlash(value, name) + err = session.Save(r, w) + if err != nil { + fmt.Println(err.Error()) + } +} + +func GetFlash(w http.ResponseWriter, r *http.Request, name string) []string { + session, err := store.Get(r, sessionFlash) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return nil + } + fm := session.Flashes(name) + if len(fm) < 0 { + return nil + } + + session.Save(r, w) + var flashes []string + for _, fl := range fm { + flashes = append(flashes, fl.(string)) + } + return flashes } func (server *Server) Run(addr string) { @@ -56,6 +88,15 @@ func (s Server) InitCommands(config AppConfig, config2 DBConfig) { } +func MakePassword(password string) (string, error) { + hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + return string(hashedPassword), err +} + +func ComparePassword(password string, hashedPassword string) bool { + return bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password)) == nil +} + func (server *Server) initializeDB(dbConfig DBConfig) { var err error dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s sslmode=disable TimeZone=Asia/Jakarta", dbConfig.DBHost, dbConfig.DBUser, dbConfig.DBPassword, dbConfig.DBName, dbConfig.DBPort) @@ -63,9 +104,10 @@ func (server *Server) initializeDB(dbConfig DBConfig) { if err != nil { panic("Failed on connecting to the database server") } + server.dbMigrate() } func (server *Server) dbMigrate() { - /*for _, model := range models.RegisterModels() { + for _, model := range models.RegisterModels() { err := server.DB.Debug().AutoMigrate(model.Model) if err != nil { @@ -74,8 +116,6 @@ func (server *Server) dbMigrate() { } fmt.Println("Database migrated successfully.") - - */ } func (server *Server) initializeAppConfig(config AppConfig) { @@ -108,3 +148,19 @@ func (server *Server) CurrentUser(w http.ResponseWriter, r *http.Request) *model return user } + +func (server *Server) routeInit() { + server.Router = mux.NewRouter() + server.Router.HandleFunc("/", server.Home).Methods("GET") + + server.Router.HandleFunc("/login", server.Login).Methods("GET") + server.Router.HandleFunc("/login", server.DoLogin).Methods("POST") + + server.Router.HandleFunc("/register", server.Register).Methods("GET") + server.Router.HandleFunc("/register", server.DoRegister).Methods("POST") + server.Router.HandleFunc("/logout", server.Logout).Methods("GET") + + staticFileDirectory := http.Dir("./assets/") + staticFileHandler := http.StripPrefix("/public/", http.FileServer(staticFileDirectory)) + server.Router.PathPrefix("/public/").Handler(staticFileHandler).Methods("GET") +} diff --git a/app/controllers/routes.go b/app/controllers/routes.go deleted file mode 100644 index 522084f..0000000 --- a/app/controllers/routes.go +++ /dev/null @@ -1,15 +0,0 @@ -package controllers - -import ( - "github.com/gorilla/mux" - "net/http" -) - -func (server *Server) initializeRoutes() { - server.Router = mux.NewRouter() - server.Router.HandleFunc("/", server.Home).Methods("GET") - - staticFileDirectory := http.Dir("./assets/") - staticFileHandler := http.StripPrefix("/public/", http.FileServer(staticFileDirectory)) - server.Router.PathPrefix("/public/").Handler(staticFileHandler).Methods("GET") -} diff --git a/app/controllers/user_controller.go b/app/controllers/user_controller.go new file mode 100644 index 0000000..7f20b09 --- /dev/null +++ b/app/controllers/user_controller.go @@ -0,0 +1,115 @@ +package controllers + +import ( + "github.com/google/uuid" + "github.com/unrolled/render" + "moretcgshop/app/models" + "net/http" +) + +func (server *Server) Login(w http.ResponseWriter, r *http.Request) { + renderer := render.New(render.Options{ + Layout: "layout", + Extensions: []string{".html", ".tmpl"}, + }) + + _ = renderer.HTML(w, http.StatusOK, "login", map[string]interface{}{ + "error": GetFlash(w, r, "error"), + }) +} + +func (server *Server) DoLogin(w http.ResponseWriter, r *http.Request) { + email := r.FormValue("email") + password := r.FormValue("password") + + userModel := models.User{} + + user, err := userModel.FindByEmail(server.DB, email) + + if err != nil { + SetFlash(w, r, "error", "Email oder Password sind nicht korrekt") + http.Redirect(w, r, "/login", http.StatusSeeOther) + return + } + + if !ComparePassword(password, user.Password) { + SetFlash(w, r, "error", "Email oder Password sind nicht korrekt") + http.Redirect(w, r, "/login", http.StatusSeeOther) + return + } + + session, _ := store.Get(r, sessionUser) + session.Values["id"] = user.ID + session.Save(r, w) + http.Redirect(w, r, "/", http.StatusSeeOther) +} + +func (server *Server) Register(w http.ResponseWriter, r *http.Request) { + renderer := render.New(render.Options{ + Layout: "layout", + Extensions: []string{".html", ",tmpl"}, + }) + + _ = renderer.HTML(w, http.StatusOK, "register", map[string]interface{}{ + "error": GetFlash(w, r, "error"), + }) +} + +func (server *Server) DoRegister(w http.ResponseWriter, r *http.Request) { + firstName := r.FormValue("first_name") + lastName := r.FormValue("last_name") + email := r.FormValue("email") + password := r.FormValue("password") + password2 := r.FormValue("password_repeat") + + if firstName == "" || lastName == "" || email == "" || password == "" || password2 == "" { + SetFlash(w, r, "error", "First name, last name, email and password are required!") + http.Redirect(w, r, "/register", http.StatusSeeOther) + return + } + + userModel := models.User{} + existUser, _ := userModel.FindByEmail(server.DB, email) + if existUser != nil { + SetFlash(w, r, "error", "Sorry, diese E-Mail wurde bereits registriert") + http.Redirect(w, r, "/register", http.StatusSeeOther) + return + } + + if password != password2 { + SetFlash(w, r, "error", "Passwörter stimmen nicht überein") + http.Redirect(w, r, "/register", http.StatusSeeOther) + return + } + + hashedPassword, _ := MakePassword(password) + + params := &models.User{ + ID: uuid.New().String(), + FirstName: firstName, + LastName: lastName, + Email: email, + Password: hashedPassword, + } + + user, err := userModel.CreateUser(server.DB, params) + if err != nil { + SetFlash(w, r, "error", "Sorry Registrierung war nicht erfolgreich") + http.Redirect(w, r, "/register", http.StatusSeeOther) + return + } + + session, _ := store.Get(r, sessionUser) + session.Values["id"] = user.ID + session.Save(r, w) + http.Redirect(w, r, "/", http.StatusSeeOther) +} + +func (server *Server) Logout(w http.ResponseWriter, r *http.Request) { + session, _ := store.Get(r, sessionUser) + + session.Values["id"] = nil + session.Save(r, w) + + http.Redirect(w, r, "/", http.StatusSeeOther) +} diff --git a/app/models/Category.go b/app/models/Category.go new file mode 100644 index 0000000..961374d --- /dev/null +++ b/app/models/Category.go @@ -0,0 +1,14 @@ +package models + +import "time" + +type Category struct { + ID string + ParentID string + Section Section + SectionID string + Products []Product `gorm:"many2many:product_categories;"` + Name string + CreatedAt time.Time + UpdatedAt time.Time +} diff --git a/app/models/Section.go b/app/models/Section.go new file mode 100644 index 0000000..f8f37a8 --- /dev/null +++ b/app/models/Section.go @@ -0,0 +1,11 @@ +package models + +import "time" + +type Section struct { + ID string + Name string + CreatedAt time.Time + UpdatedAt time.Time + Categories []Category +} diff --git a/app/models/models.go b/app/models/models.go new file mode 100644 index 0000000..4264cf7 --- /dev/null +++ b/app/models/models.go @@ -0,0 +1,14 @@ +package models + +type Model struct { + Model interface{} +} + +func RegisterModels() []Model { + return []Model{ + {Model: User{}}, + {Model: Product{}}, + {Model: Category{}}, + {Model: Section{}}, + } +} diff --git a/app/models/product.go b/app/models/product.go new file mode 100644 index 0000000..abba99d --- /dev/null +++ b/app/models/product.go @@ -0,0 +1,16 @@ +package models + +import ( + "gorm.io/gorm" + "time" +) + +type Product struct { + ID string + ParentID string + Name string + Categories []Category `gorm:"many2many:product_categories;"` + CreatedAt time.Time + UpdatedAt time.Time + DeletedAt gorm.DeletedAt +} diff --git a/app/models/user.go b/app/models/user.go index 66cbd7b..ae447e7 100644 --- a/app/models/user.go +++ b/app/models/user.go @@ -2,6 +2,7 @@ package models import ( "gorm.io/gorm" + "strings" "time" ) @@ -19,9 +20,7 @@ type User struct { } func (u *User) FindByID(db *gorm.DB, userID string) (*User, error) { - var user User - err := db.Debug().Model(User{}).Where("id = ?", userID). First(&user).Error if err != nil { @@ -29,3 +28,29 @@ func (u *User) FindByID(db *gorm.DB, userID string) (*User, error) { } return &user, nil } + +func (u *User) FindByEmail(db *gorm.DB, email string) (*User, error) { + var user User + err := db.Debug().Model(User{}).Where("LOWER(email) = ?", strings.ToLower(email)). + First(&user).Error + if err != nil { + return nil, err + } + return &user, nil +} + +func (u *User) CreateUser(db *gorm.DB, param *User) (*User, error) { + user := &User{ + ID: param.ID, + FirstName: param.FirstName, + LastName: param.LastName, + Email: param.Email, + Password: param.Password, + } + + err := db.Debug().Create(&user).Error + if err != nil { + return nil, err + } + return user, nil +} diff --git a/app/server.go b/app/server.go index 03062cd..0ec711b 100644 --- a/app/server.go +++ b/app/server.go @@ -31,9 +31,9 @@ func Run() { appConfig.AppURL = getEnv("APP_URL", "http://localhost:9000") dbConfig.DBHost = getEnv("DB_HOST", "localhost") - dbConfig.DBUser = getEnv("DB_USER", "user") - dbConfig.DBPassword = getEnv("DB_PASSWORD", "password") - dbConfig.DBName = getEnv("DB_NAME", "dbname") + dbConfig.DBUser = getEnv("DB_USER", "moretcg") + dbConfig.DBPassword = getEnv("DB_PASSWORD", "moretcg") + dbConfig.DBName = getEnv("DB_NAME", "moretcg") dbConfig.DBPort = getEnv("DB_PORT", "5432") dbConfig.DBDriver = getEnv("DB_DRIVER", "postgres") diff --git a/go.mod b/go.mod index 77893d8..b722241 100644 --- a/go.mod +++ b/go.mod @@ -3,10 +3,12 @@ module moretcgshop go 1.20 require ( + github.com/google/uuid v1.4.0 github.com/gorilla/mux v1.8.1 github.com/gorilla/sessions v1.2.2 github.com/joho/godotenv v1.5.1 github.com/unrolled/render v1.6.1 + golang.org/x/crypto v0.14.0 gorm.io/driver/postgres v1.5.4 gorm.io/gorm v1.25.5 ) @@ -19,7 +21,6 @@ require ( github.com/jackc/pgx/v5 v5.4.3 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect - golang.org/x/crypto v0.14.0 // indirect golang.org/x/sys v0.13.0 // indirect golang.org/x/text v0.13.0 // indirect ) diff --git a/go.sum b/go.sum index 25e7006..e573273 100644 --- a/go.sum +++ b/go.sum @@ -3,6 +3,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= diff --git a/templates/pages/login.html b/templates/pages/login.html new file mode 100644 index 0000000..07e63bb --- /dev/null +++ b/templates/pages/login.html @@ -0,0 +1,76 @@ +{{ define "login" }} + +