Refactoring handlers into appropriate files.
This commit is contained in:
parent
06755693a1
commit
d0eb8f733c
|
@ -0,0 +1,146 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
// Define contextKey globally within the package
|
||||
type contextKey string
|
||||
|
||||
// Define your key as a constant of the custom type
|
||||
const userKey contextKey = "user"
|
||||
|
||||
// LoginRequest represents the request body for the /login endpoint.
|
||||
type LoginRequest struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
// LoginResponse represents the response body for the /login endpoint.
|
||||
type LoginResponse struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
fmt.Printf("handlers.go init: config = %+v", config)
|
||||
}
|
||||
|
||||
// loginHandler handles the /login endpoint.
|
||||
// LoginHandler handles the /login endpoint.
|
||||
func LoginHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var req LoginRequest
|
||||
//fmt.Println("db is ", db)
|
||||
//fmt.Println("config is ", config)
|
||||
if db == nil {
|
||||
fmt.Println("DB is nil")
|
||||
http.Error(w, "Database not initialized", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
fmt.Println("DB is not nil")
|
||||
|
||||
//if config == nil {
|
||||
//fmt.Println("Config is nil in LoginHandler")
|
||||
//h//ttp.Error(w, "Configuration not loaded", http.StatusInternalServerError)
|
||||
//return
|
||||
//}
|
||||
//fmt.Println("Config is not nil")
|
||||
|
||||
fmt.Printf("DB: %+v\n", db)
|
||||
//fmt.Printf("Config: %+v\n", config)
|
||||
fmt.Println("LoginHandler called")
|
||||
err := json.NewDecoder(r.Body).Decode(&req)
|
||||
if err != nil {
|
||||
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the user exists and the password matches
|
||||
var user User
|
||||
db.Where("username = ?", req.Username).First(&user)
|
||||
if user.ID == 0 {
|
||||
http.Error(w, "Invalid username or password", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
// Compare the provided password with the stored hashed password
|
||||
err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.Password))
|
||||
if err != nil {
|
||||
http.Error(w, "Invalid username or password", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
// Generate JWT token
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
||||
"username": user.Username,
|
||||
"exp": time.Now().Add(time.Hour * 24).Unix(), // Token expires in 24 hours
|
||||
})
|
||||
|
||||
tokenString, err := token.SignedString(*JWTSecret)
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to generate token", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Return the token in the response
|
||||
json.NewEncoder(w).Encode(LoginResponse{Token: tokenString})
|
||||
}
|
||||
|
||||
// authMiddleware is a middleware function that checks for a valid JWT token in the request header and enables CORS.
|
||||
func AuthMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// Set CORS headers
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*") // Replace "*" with your allowed frontend origin if needed
|
||||
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
|
||||
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
|
||||
|
||||
// Handle preflight request for CORS
|
||||
if r.Method == http.MethodOptions {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
// Get the token from the request header
|
||||
tokenString := r.Header.Get("Authorization")
|
||||
if tokenString == "" {
|
||||
http.Error(w, "Authorization header missing", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
// Remove "Bearer " prefix from token string
|
||||
tokenString = strings.Replace(tokenString, "Bearer ", "", 1)
|
||||
|
||||
// Parse and validate the JWT token
|
||||
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||||
// Make sure that the signing method is HMAC
|
||||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
|
||||
}
|
||||
return *JWTSecret, nil
|
||||
})
|
||||
if err != nil || !token.Valid {
|
||||
http.Error(w, "Invalid token", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
// Extract the user claims from the token
|
||||
if claims, ok := token.Claims.(jwt.MapClaims); ok {
|
||||
// Add the "user" claim to the request context
|
||||
newCtx := context.WithValue(r.Context(), userKey, claims["username"])
|
||||
r = r.WithContext(newCtx)
|
||||
} else {
|
||||
http.Error(w, "Invalid token claims", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
// Call the next handler in the chain
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
2024/10/26 20:47:15 main.go:94: Logging initialized
|
||||
2024/10/26 20:47:15 main.go:106: Connected to database in connectToDatabase
|
||||
2024/10/26 20:47:15 main.go:116: Static directory error: static directory does not exist: ./build/
|
||||
2024/10/26 20:47:52 main.go:94: Logging initialized
|
||||
2024/10/26 20:47:52 main.go:106: Connected to database in connectToDatabase
|
||||
2024/10/26 20:47:52 main.go:116: Static directory error: static directory does not exist: ./build/
|
||||
2024/10/26 20:49:06 main.go:94: Logging initialized
|
||||
2024/10/26 20:49:06 main.go:106: Connected to database in connectToDatabase
|
||||
2024/10/26 22:19:11 main.go:94: Logging initialized
|
||||
2024/10/26 22:19:11 main.go:106: Connected to database in connectToDatabase
|
|
@ -0,0 +1,76 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
// getBoxesHandler handles the GET /boxes endpoint.
|
||||
func GetBoxesHandler(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Printf("Received %s request to %s\n", r.Method, r.URL)
|
||||
var boxes []Box
|
||||
db.Find(&boxes)
|
||||
json.NewEncoder(w).Encode(boxes)
|
||||
}
|
||||
|
||||
func GetBoxHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
id := vars["id"]
|
||||
|
||||
var box Box
|
||||
if err := db.First(&box, id).Error; err != nil {
|
||||
http.Error(w, "Box not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(box)
|
||||
}
|
||||
|
||||
// createBoxHandler handles the POST /boxes endpoint.
|
||||
func CreateBoxHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var box Box
|
||||
err := json.NewDecoder(r.Body).Decode(&box)
|
||||
if err != nil {
|
||||
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
db.Create(&box)
|
||||
|
||||
// Create a response struct to include the ID
|
||||
type createBoxResponse struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
response := createBoxResponse{
|
||||
ID: box.ID,
|
||||
Name: box.Name,
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
// deleteBoxHandler handles the DELETE /boxes/{id} endpoint.
|
||||
func DeleteBoxHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
id := vars["id"]
|
||||
|
||||
// Retrieve the box from the database
|
||||
var box Box
|
||||
if err := db.First(&box, id).Error; err != nil {
|
||||
http.Error(w, "Box not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
// Optionally, delete associated items (if you want cascading delete)
|
||||
// db.Where("box_id = ?", id).Delete(&Item{})
|
||||
|
||||
// Delete the box
|
||||
db.Delete(&box)
|
||||
|
||||
w.WriteHeader(http.StatusNoContent) // 204 No Content
|
||||
}
|
|
@ -1,167 +1,26 @@
|
|||
// item_handlers.go
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/gorilla/mux"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
// Define contextKey globally within the package
|
||||
type contextKey string
|
||||
|
||||
// Define your key as a constant of the custom type
|
||||
const userKey contextKey = "user"
|
||||
|
||||
// LoginRequest represents the request body for the /login endpoint.
|
||||
type LoginRequest struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
// LoginResponse represents the response body for the /login endpoint.
|
||||
type LoginResponse struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
fmt.Printf("handlers.go init: config = %+v", config)
|
||||
}
|
||||
|
||||
// loginHandler handles the /login endpoint.
|
||||
// LoginHandler handles the /login endpoint.
|
||||
func LoginHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var req LoginRequest
|
||||
//fmt.Println("db is ", db)
|
||||
//fmt.Println("config is ", config)
|
||||
if db == nil {
|
||||
fmt.Println("DB is nil")
|
||||
http.Error(w, "Database not initialized", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
fmt.Println("DB is not nil")
|
||||
|
||||
//if config == nil {
|
||||
//fmt.Println("Config is nil in LoginHandler")
|
||||
//h//ttp.Error(w, "Configuration not loaded", http.StatusInternalServerError)
|
||||
//return
|
||||
//}
|
||||
//fmt.Println("Config is not nil")
|
||||
|
||||
fmt.Printf("DB: %+v\n", db)
|
||||
//fmt.Printf("Config: %+v\n", config)
|
||||
fmt.Println("LoginHandler called")
|
||||
err := json.NewDecoder(r.Body).Decode(&req)
|
||||
if err != nil {
|
||||
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the user exists and the password matches
|
||||
var user User
|
||||
db.Where("username = ?", req.Username).First(&user)
|
||||
if user.ID == 0 {
|
||||
http.Error(w, "Invalid username or password", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
// Compare the provided password with the stored hashed password
|
||||
err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.Password))
|
||||
if err != nil {
|
||||
http.Error(w, "Invalid username or password", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
// Generate JWT token
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
||||
"username": user.Username,
|
||||
"exp": time.Now().Add(time.Hour * 24).Unix(), // Token expires in 24 hours
|
||||
})
|
||||
|
||||
tokenString, err := token.SignedString(*JWTSecret)
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to generate token", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Return the token in the response
|
||||
json.NewEncoder(w).Encode(LoginResponse{Token: tokenString})
|
||||
}
|
||||
|
||||
// getBoxesHandler handles the GET /boxes endpoint.
|
||||
func GetBoxesHandler(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Printf("Received %s request to %s\n", r.Method, r.URL)
|
||||
var boxes []Box
|
||||
db.Find(&boxes)
|
||||
json.NewEncoder(w).Encode(boxes)
|
||||
}
|
||||
|
||||
func GetBoxHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
id := vars["id"]
|
||||
|
||||
var box Box
|
||||
if err := db.First(&box, id).Error; err != nil {
|
||||
http.Error(w, "Box not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(box)
|
||||
}
|
||||
|
||||
// createBoxHandler handles the POST /boxes endpoint.
|
||||
func CreateBoxHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var box Box
|
||||
err := json.NewDecoder(r.Body).Decode(&box)
|
||||
if err != nil {
|
||||
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
db.Create(&box)
|
||||
|
||||
// Create a response struct to include the ID
|
||||
type createBoxResponse struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
response := createBoxResponse{
|
||||
ID: box.ID,
|
||||
Name: box.Name,
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
// deleteBoxHandler handles the DELETE /boxes/{id} endpoint.
|
||||
func DeleteBoxHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
id := vars["id"]
|
||||
|
||||
// Retrieve the box from the database
|
||||
var box Box
|
||||
if err := db.First(&box, id).Error; err != nil {
|
||||
http.Error(w, "Box not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
// Optionally, delete associated items (if you want cascading delete)
|
||||
// db.Where("box_id = ?", id).Delete(&Item{})
|
||||
|
||||
// Delete the box
|
||||
db.Delete(&box)
|
||||
|
||||
w.WriteHeader(http.StatusNoContent) // 204 No Content
|
||||
}
|
||||
// Move all item-related handlers here:
|
||||
// - GetItemsHandler
|
||||
// - CreateItemHandler
|
||||
// - GetItemHandler
|
||||
// - UpdateItemHandler
|
||||
// - DeleteItemHandler
|
||||
// - UploadItemImageHandler
|
||||
// - GetItemImageHandler
|
||||
// - SearchItemsHandler
|
||||
// - GetItemsInBoxHandler
|
||||
|
||||
// getItemsHandler handles the GET /items endpoint.
|
||||
func GetItemsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -380,55 +239,3 @@ func DeleteItemHandler(w http.ResponseWriter, r *http.Request) {
|
|||
db.Delete(&item)
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// authMiddleware is a middleware function that checks for a valid JWT token in the request header and enables CORS.
|
||||
func AuthMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// Set CORS headers
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*") // Replace "*" with your allowed frontend origin if needed
|
||||
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
|
||||
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
|
||||
|
||||
// Handle preflight request for CORS
|
||||
if r.Method == http.MethodOptions {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
// Get the token from the request header
|
||||
tokenString := r.Header.Get("Authorization")
|
||||
if tokenString == "" {
|
||||
http.Error(w, "Authorization header missing", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
// Remove "Bearer " prefix from token string
|
||||
tokenString = strings.Replace(tokenString, "Bearer ", "", 1)
|
||||
|
||||
// Parse and validate the JWT token
|
||||
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||||
// Make sure that the signing method is HMAC
|
||||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
|
||||
}
|
||||
return *JWTSecret, nil
|
||||
})
|
||||
if err != nil || !token.Valid {
|
||||
http.Error(w, "Invalid token", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
// Extract the user claims from the token
|
||||
if claims, ok := token.Claims.(jwt.MapClaims); ok {
|
||||
// Add the "user" claim to the request context
|
||||
newCtx := context.WithValue(r.Context(), userKey, claims["username"])
|
||||
r = r.WithContext(newCtx)
|
||||
} else {
|
||||
http.Error(w, "Invalid token claims", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
// Call the next handler in the chain
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue