phase 2 initial test

This commit is contained in:
2026-05-06 23:39:04 +02:00
parent 8592fc5d65
commit 57991e5406
17 changed files with 720 additions and 133 deletions
+90 -5
View File
@@ -5,24 +5,91 @@ import (
"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(store sessions.Store) *UserHandler {
return &UserHandler{store: store}
func NewUserHandler(db *xorm.Engine, store sessions.Store) *UserHandler {
return &UserHandler{db: db, store: store}
}
func (h *UserHandler) Me(w http.ResponseWriter, r *http.Request) {
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")
json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
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(map[string]string{"status": "ok"})
json.NewEncoder(w).Encode(user)
}
func (h *UserHandler) Logout(w http.ResponseWriter, r *http.Request) {
@@ -31,3 +98,21 @@ func (h *UserHandler) Logout(w http.ResponseWriter, r *http.Request) {
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)
}