package handlers import ( "encoding/json" "net/http" "github.com/gorilla/sessions" "golang.org/x/crypto/bcrypt" "xorm.io/xorm" "github.com/forgeo/forgebucket/internal/api/middleware" "github.com/forgeo/forgebucket/internal/models" ) type UserHandler struct { db *xorm.Engine store sessions.Store } func NewUserHandler(db *xorm.Engine, store sessions.Store) *UserHandler { return &UserHandler{db: db, store: store} } func (h *UserHandler) Register(w http.ResponseWriter, r *http.Request) { var body struct { Username string `json:"username"` Email string `json:"email"` Password string `json:"password"` } if err := json.NewDecoder(r.Body).Decode(&body); err != nil { jsonError(w, "invalid request body", http.StatusBadRequest) return } if body.Username == "" || body.Email == "" || body.Password == "" { jsonError(w, "username, email, and password are required", http.StatusBadRequest) return } hash, err := bcrypt.GenerateFromPassword([]byte(body.Password), bcrypt.DefaultCost) if err != nil { jsonError(w, "internal error", http.StatusInternalServerError) return } user := &models.User{ Username: body.Username, Email: body.Email, PasswordHash: string(hash), } if _, err := h.db.Insert(user); err != nil { jsonError(w, "username or email already taken", http.StatusConflict) return } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) json.NewEncoder(w).Encode(user) } func (h *UserHandler) Login(w http.ResponseWriter, r *http.Request) { var body struct { Username string `json:"username"` Password string `json:"password"` } if err := json.NewDecoder(r.Body).Decode(&body); err != nil { jsonError(w, "invalid request body", http.StatusBadRequest) return } var user models.User found, err := h.db.Where("username = ?", body.Username).Get(&user) if err != nil || !found { jsonError(w, "invalid credentials", http.StatusUnauthorized) return } if err := bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(body.Password)); err != nil { jsonError(w, "invalid credentials", http.StatusUnauthorized) return } session, _ := h.store.Get(r, "fb_session") session.Values["userID"] = user.ID session.Values["username"] = user.Username session.Values["isAdmin"] = user.IsAdmin if err := session.Save(r, w); err != nil { jsonError(w, "could not save session", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(user) } func (h *UserHandler) Logout(w http.ResponseWriter, r *http.Request) { session, _ := h.store.Get(r, "fb_session") session.Options.MaxAge = -1 session.Save(r, w) w.WriteHeader(http.StatusNoContent) } func (h *UserHandler) Me(w http.ResponseWriter, r *http.Request) { userID, ok := middleware.UserIDFromContext(r.Context()) if !ok { jsonError(w, "unauthorized", http.StatusUnauthorized) return } var user models.User found, err := h.db.ID(userID).Get(&user) if err != nil || !found { jsonError(w, "user not found", http.StatusNotFound) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(user) }