package main import ( "encoding/json" "fmt" "log" "net/http" "os" "time" "wishlify/auth" "wishlify/model" "wishlify/router" "github.com/google/uuid" "golang.org/x/crypto/bcrypt" "gorm.io/driver/sqlite" "gorm.io/gorm" ) func main() { dbPath := os.Getenv("DB_PATH") if dbPath == "" { dbPath = "./sqlite.db" } db, err := gorm.Open(sqlite.Open("./sqlite.db")) if err != nil { log.Fatalf("failed to connect to db: %v\n", err) } if err := db.AutoMigrate(&model.User{}, &model.Wishlist{}); err != nil { log.Fatalf("failed to migrate db: %v\n", err) } router := router.New() router.Handle("POST /auth/register", func(w http.ResponseWriter, r *http.Request) error { var body struct { Email string `json:"email"` Login string `json:"login"` Password string `json:"password"` } if err := json.NewDecoder(r.Body).Decode(&body); err != nil { w.WriteHeader(500) return fmt.Errorf("failed to decode request body: %v", err) } if body.Email == "" || body.Login == "" || body.Password == "" { w.WriteHeader(400) return fmt.Errorf("invalid request; email, login and password are required") } hash, err := bcrypt.GenerateFromPassword([]byte(body.Password), 10) if err != nil { w.WriteHeader(500) return fmt.Errorf("failed to generate password hash: %v", err) } user := &model.User{ Email: body.Email, Login: body.Login, Password: string(hash), } if tx := db.Create(user); tx.Error != nil { w.WriteHeader(400) return fmt.Errorf("failed to create user: %v", tx.Error) } expiryTime := time.Now().Add(time.Hour * 24 * 7) token, err := auth.GenerateUserToken(user.ID, expiryTime) if err != nil { w.WriteHeader(500) return fmt.Errorf("failed to generate jwt: %v", err) } auth.SetUserCookie(w, token, expiryTime) w.WriteHeader(201) return router.SendJSON(w, user) }) router.Handle("POST /auth/login", func(w http.ResponseWriter, r *http.Request) error { var body struct { Login string `json:"login"` Password string `json:"password"` } if err := json.NewDecoder(r.Body).Decode(&body); err != nil { w.WriteHeader(400) return fmt.Errorf("failed to decode request body: %v", err) } if body.Login == "" || body.Password == "" { w.WriteHeader(400) return fmt.Errorf("invalid request; login and password are required") } user := &model.User{} if tx := db.First(user, "login = ?", body.Login); tx.Error != nil { w.WriteHeader(404) return fmt.Errorf("user not found: %v", tx.Error) } if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(body.Password)); err != nil { w.WriteHeader(400) return fmt.Errorf("invalid password: %v", err) } expiryTime := time.Now().Add(time.Hour * 24 * 7) token, err := auth.GenerateUserToken(user.ID, expiryTime) if err != nil { w.WriteHeader(500) return fmt.Errorf("failed to generate jwt: %v", err) } auth.SetUserCookie(w, token, expiryTime) w.WriteHeader(200) return router.SendJSON(w, user) }) router.Handle("POST /auth/logout", func(w http.ResponseWriter, r *http.Request) error { auth.RemoveUserCookie(w) w.WriteHeader(200) return router.SendJSON(w, struct { Ok bool `json:"ok"` }{true}) }) router.Handle("GET /user", func(w http.ResponseWriter, r *http.Request) error { userId, err := auth.GetUserIdFromRequest(r) if err != nil { w.WriteHeader(401) return fmt.Errorf("unauthorized: %v", err) } user := &model.User{} if tx := db.First(user, "id = ?", userId); tx.Error != nil { w.WriteHeader(404) return fmt.Errorf("user not found: %v", tx.Error) } return router.SendJSON(w, user) }) router.Handle("GET /user/wishlists", func(w http.ResponseWriter, r *http.Request) error { userId, err := auth.GetUserIdFromRequest(r) if err != nil { w.WriteHeader(401) return fmt.Errorf("unauthorized: %v", err) } wishlists := []model.Wishlist{} if tx := db.Find(&wishlists, "user_id = ?", userId); tx.Error != nil { w.WriteHeader(404) return fmt.Errorf("no wishlists found: %v", err) } w.WriteHeader(200) return router.SendJSON(w, wishlists) }) router.Handle("POST /user/wishlists", func(w http.ResponseWriter, r *http.Request) error { userId, err := auth.GetUserIdFromRequest(r) if err != nil { w.WriteHeader(401) return fmt.Errorf("unauthorized: %v", err) } var body struct { Name string `json:"name"` Description string `json:"description"` } if err := json.NewDecoder(r.Body).Decode(&body); err != nil { w.WriteHeader(400) return fmt.Errorf("failed to decode request body: %v", err) } if body.Name == "" { w.WriteHeader(400) return fmt.Errorf("invalid request; name is required") } uid, err := uuid.NewV7() if err != nil { w.WriteHeader(500) return fmt.Errorf("failed to generate uuid for new wishlist: %v", err) } wishlist := &model.Wishlist{ UUID: uid.String(), Name: body.Name, Description: body.Description, UserID: userId, } if tx := db.Create(wishlist); tx.Error != nil { w.WriteHeader(500) return fmt.Errorf("failed to create wishlist: %v", tx.Error) } return router.SendJSON(w, wishlist) }) router.Handle("GET /wishlists/{uid}", func(w http.ResponseWriter, r *http.Request) error { uid := r.PathValue("uid") wishlist := &model.Wishlist{} if tx := db.First(wishlist, "uuid = ?", uid); tx.Error != nil { w.WriteHeader(404) return fmt.Errorf("wishlist not found: %v", tx.Error) } w.WriteHeader(200) return router.SendJSON(w, wishlist) }) router.Handle("DELETE /user/wishlists/{uid}", func(w http.ResponseWriter, r *http.Request) error { userId, err := auth.GetUserIdFromRequest(r) if err != nil { w.WriteHeader(401) return fmt.Errorf("unauthorized: %v", err) } uid := r.PathValue("uid") if tx := db.Delete(&model.Wishlist{}, "uuid = ? AND user_id = ?", uid, userId); tx.Error != nil { w.WriteHeader(404) return fmt.Errorf("failed to delete wishlist: %v", tx.Error) } return router.SendJSON(w, struct { Ok bool `json:"ok"` }{true}) }) log.Println("starting http server") if err := http.ListenAndServe(":5000", router.Mux()); err != nil { log.Fatalf("failed to start http server: %v\n", err) } }