diff --git a/handlers/account.go b/handlers/account.go index 40930ef..d00b9cf 100644 --- a/handlers/account.go +++ b/handlers/account.go @@ -18,20 +18,20 @@ var cfg, _ = config.LoadConfig() func Register(w http.ResponseWriter, r *http.Request) { var user models.User if err := json.NewDecoder(r.Body).Decode(&user); err != nil { - log.Println("[api/register] " + r.RemoteAddr + ": " + err.Error()) + log.Println("POST [api/register] " + r.RemoteAddr + ": " + err.Error()) http.Error(w, "Invalid request body", http.StatusBadRequest) return } if user.Username == "" || user.Password == "" { - log.Println("[api/register] " + r.RemoteAddr + ": Username or Password is empty") + log.Println("POST [api/register] " + r.RemoteAddr + ": Username or Password is empty") http.Error(w, "username and password required", http.StatusBadRequest) return } hashed, err := auth.HashPassword(user.Password) if err != nil { - log.Println("[api/register] " + r.RemoteAddr + ": " + err.Error()) + log.Println("POST [api/register] " + r.RemoteAddr + ": " + err.Error()) http.Error(w, "internal error", http.StatusInternalServerError) return } @@ -40,13 +40,13 @@ func Register(w http.ResponseWriter, r *http.Request) { user.Role = "user" if err := storage.AddUser(&user); err != nil { - log.Println("[api/register] " + r.RemoteAddr + ": " + err.Error()) + log.Println("POST [api/register] " + r.RemoteAddr + ": " + err.Error()) http.Error(w, "user already exists", http.StatusBadRequest) return } w.WriteHeader(http.StatusCreated) - log.Println("[api/register] " + r.RemoteAddr + ": Successfully created user") + log.Println("POST [api/register] " + r.RemoteAddr + ": Successfully created user") } func Login(w http.ResponseWriter, r *http.Request) { var creds struct { @@ -54,41 +54,41 @@ func Login(w http.ResponseWriter, r *http.Request) { Password string `json:"password"` } if err := json.NewDecoder(r.Body).Decode(&creds); err != nil { - log.Println("[api/login] " + r.RemoteAddr + ": " + err.Error()) + log.Println("POST [api/login] " + r.RemoteAddr + ": " + err.Error()) http.Error(w, "Invalid request", http.StatusBadRequest) return } user, err := storage.GetUserByUsername(creds.Username) if err != nil { - log.Println("[api/login] " + r.RemoteAddr + ": " + err.Error()) + log.Println("POST [api/login] " + r.RemoteAddr + ": " + err.Error()) http.Error(w, "Invalid credentials", http.StatusUnauthorized) return } if !auth.CheckPasswordHash(creds.Password, user.Password) { - log.Println("[api/login] " + r.RemoteAddr + ": Invalid credentials") + log.Println("POST [api/login] " + r.RemoteAddr + ": Invalid credentials") http.Error(w, "Invalid credentials", http.StatusUnauthorized) return } secret := []byte(os.Getenv("SHAP_JWT_SECRET")) if len(secret) == 0 { - log.Println("[api/login] " + r.RemoteAddr + ": Server misconfiguration") + log.Println("POST [api/login] " + r.RemoteAddr + ": Server misconfiguration") http.Error(w, "Server misconfiguration", http.StatusInternalServerError) return } accessToken, err := auth.GenerateJWT(user.ID, user.Role, secret) if err != nil { - log.Println("[api/login] " + r.RemoteAddr + ": " + err.Error()) + log.Println("POST [api/login] " + r.RemoteAddr + ": " + err.Error()) http.Error(w, "Could not generate token", http.StatusInternalServerError) return } refreshTokenPlain, err := utils.GenerateRefreshToken() if err != nil { - log.Println("[api/login] " + r.RemoteAddr + ": " + err.Error()) + log.Println("POST [api/login] " + r.RemoteAddr + ": " + err.Error()) http.Error(w, "could not generate refresh token", http.StatusInternalServerError) return } @@ -107,7 +107,7 @@ func Login(w http.ResponseWriter, r *http.Request) { CreatedAt: time.Now().Unix(), Revoked: false, }); err != nil { - log.Println("[api/login] " + r.RemoteAddr + ": " + err.Error()) + log.Println("POST [api/login] " + r.RemoteAddr + ": " + err.Error()) http.Error(w, "could not save refresh token", http.StatusInternalServerError) return } @@ -127,17 +127,17 @@ func Login(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") err = json.NewEncoder(w).Encode(resp) if err != nil { - log.Println("[api/login] " + r.RemoteAddr + ": " + err.Error()) + log.Println("POST [api/login] " + r.RemoteAddr + ": " + err.Error()) http.Error(w, "Something went wrong", http.StatusInternalServerError) return } - log.Println("[api/login] " + r.RemoteAddr + ": Successfully logged in") + log.Println("POST [api/login] " + r.RemoteAddr + ": Successfully logged in") } func Logout(w http.ResponseWriter, r *http.Request) { claims := r.Context().Value(auth.UserContextKey).(*auth.Claims) err := storage.RevokeAllRefreshTokensForUser(claims.UserID) if err != nil { - log.Println("[api/logout] " + r.RemoteAddr + ": " + err.Error()) + log.Println("GET [api/logout] " + r.RemoteAddr + ": " + err.Error()) http.Error(w, "Internal server error", http.StatusInternalServerError) return } @@ -150,20 +150,20 @@ func TestHandler(w http.ResponseWriter, r *http.Request) { err := json.NewEncoder(w).Encode(map[string]interface{}{ "user_id": claims.UserID, "role": claims.Role, - "msg": "access granted to protected endpoint", + "msg": "Authentication successful", }) if err != nil { - log.Println("[api/ping] " + r.RemoteAddr + ": " + err.Error()) + log.Println("GET [api/ping] " + r.RemoteAddr + ": " + err.Error()) return } - log.Println("[api/login] " + r.RemoteAddr + ": Successfully tested connection") + log.Println("GET [api/login] " + r.RemoteAddr + ": Successfully tested connection") } func RefreshToken(w http.ResponseWriter, r *http.Request) { var req struct { RefreshToken string `json:"refresh_token"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - log.Println("[api/refresh] " + r.RemoteAddr + ": " + err.Error()) + log.Println("POST [api/refresh] " + r.RemoteAddr + ": " + err.Error()) http.Error(w, "Invalid request", http.StatusBadRequest) return } @@ -172,7 +172,7 @@ func RefreshToken(w http.ResponseWriter, r *http.Request) { tokenRow, err := storage.GetRefreshToken(hashed) if err != nil || tokenRow.Revoked || tokenRow.ExpiresAt < time.Now().Unix() { - log.Println("[api/refresh] " + r.RemoteAddr + ": Invalid refresh token") + log.Println("POST [api/refresh] " + r.RemoteAddr + ": Invalid refresh token") http.Error(w, "Invalid refresh token", http.StatusUnauthorized) return } @@ -195,7 +195,7 @@ func RefreshToken(w http.ResponseWriter, r *http.Request) { Revoked: false, DeviceInfo: deviceInfo, }); err != nil { - log.Println("[api/refresh] " + r.RemoteAddr + ": " + err.Error()) + log.Println("POST [api/refresh] " + r.RemoteAddr + ": " + err.Error()) http.Error(w, "Could not generate new refresh token", http.StatusInternalServerError) return } @@ -206,7 +206,7 @@ func RefreshToken(w http.ResponseWriter, r *http.Request) { "access_token": accessToken, "refresh_token": newToken, }); err != nil { - log.Println("[api/refresh] " + r.RemoteAddr + ": " + err.Error()) + log.Println("POST [api/refresh] " + r.RemoteAddr + ": " + err.Error()) http.Error(w, "Internal server error", http.StatusInternalServerError) return } diff --git a/handlers/expenses.go b/handlers/expenses.go index 6e6e4bd..8d3e299 100644 --- a/handlers/expenses.go +++ b/handlers/expenses.go @@ -2,10 +2,12 @@ package handlers import ( "encoding/json" + "log" "net/http" "shap-planner-backend/models" "shap-planner-backend/storage" "shap-planner-backend/utils" + "time" ) func Expenses(w http.ResponseWriter, r *http.Request) { @@ -20,37 +22,54 @@ func Expenses(w http.ResponseWriter, r *http.Request) { Shares []models.ExpenseShare `json:"shares"` } if err := json.NewDecoder(r.Body).Decode(&body); err != nil { + log.Println("POST [api/expense] " + r.RemoteAddr + ": " + err.Error()) http.Error(w, "Invalid request body", http.StatusBadRequest) return } if claims.UserID != body.Expense.PayerID { // You cannot create an expense in the name of another user - http.Error(w, "Invalid request", http.StatusUnauthorized) + log.Println("POST [api/expense] " + r.RemoteAddr + ": claims.UserID and expense.PayerID does not match") + http.Error(w, "Invalid request", http.StatusBadRequest) return } + // Set ExpenseID if body.Expense.ID != "" { - http.Error(w, "Invalid request", http.StatusUnauthorized) + log.Println("POST [api/expense] " + r.RemoteAddr + ": Expense ID must be empty") + http.Error(w, "Invalid request", http.StatusBadRequest) return } body.Expense.ID = utils.GenerateUUID() + if body.Expense.CreatedAt == 0 { + body.Expense.CreatedAt = time.Now().Unix() + } + if body.Expense.Amount <= 0 { + log.Println("POST [api/expense] " + r.RemoteAddr + ": Amount must be greater than zero") + http.Error(w, "Invalid request", http.StatusBadRequest) + return + } + // Set ShareIDs and save them for _, share := range body.Shares { if share.ID != "" { - http.Error(w, "Invalid request", http.StatusUnauthorized) + log.Println("POST [api/expense] " + r.RemoteAddr + ": Share ID must be empty") + http.Error(w, "Invalid request", http.StatusBadRequest) return } if share.ExpenseID != "" { - http.Error(w, "Invalid request", http.StatusUnauthorized) + log.Println("POST [api/expense] " + r.RemoteAddr + ": Expense ID of Share must be empty") + http.Error(w, "Invalid request", http.StatusBadRequest) return } share.ExpenseID = body.Expense.ID share.ID = utils.GenerateUUID() err := storage.AddShare(&share) if err != nil { + log.Println("POST [api/expense] " + r.RemoteAddr + ": " + err.Error()) http.Error(w, "Error adding expense", http.StatusBadRequest) // Should never happen return } } err := storage.AddExpense(&body.Expense) if err != nil { + log.Println("POST [api/expense] " + r.RemoteAddr + ": " + err.Error()) http.Error(w, "Error adding expense", http.StatusBadRequest) return } @@ -59,13 +78,16 @@ func Expenses(w http.ResponseWriter, r *http.Request) { "shares": body.Shares, }) if err != nil { - println(err.Error()) + log.Println("POST [api/expense] " + r.RemoteAddr + ": " + err.Error()) return } + log.Println("POST [api/expense] " + r.RemoteAddr + ": Successfully added expense and its shares") break case http.MethodPut: // -> Update Expense break case http.MethodDelete: // -> Delete Expense + default: + http.Error(w, "Invalid request method", http.StatusMethodNotAllowed) } } func AdminPanel(w http.ResponseWriter, r *http.Request) {}