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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/dgrijalva/jwt-go"
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"golang.org/x/crypto/bcrypt"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Define contextKey globally within the package
|
// Move all item-related handlers here:
|
||||||
type contextKey string
|
// - GetItemsHandler
|
||||||
|
// - CreateItemHandler
|
||||||
// Define your key as a constant of the custom type
|
// - GetItemHandler
|
||||||
const userKey contextKey = "user"
|
// - UpdateItemHandler
|
||||||
|
// - DeleteItemHandler
|
||||||
// LoginRequest represents the request body for the /login endpoint.
|
// - UploadItemImageHandler
|
||||||
type LoginRequest struct {
|
// - GetItemImageHandler
|
||||||
Username string `json:"username"`
|
// - SearchItemsHandler
|
||||||
Password string `json:"password"`
|
// - GetItemsInBoxHandler
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// getItemsHandler handles the GET /items endpoint.
|
// getItemsHandler handles the GET /items endpoint.
|
||||||
func GetItemsHandler(w http.ResponseWriter, r *http.Request) {
|
func GetItemsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -380,55 +239,3 @@ func DeleteItemHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
db.Delete(&item)
|
db.Delete(&item)
|
||||||
w.WriteHeader(http.StatusNoContent)
|
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