119 lines
3.1 KiB
Go
119 lines
3.1 KiB
Go
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)
|
|
}
|