feat: v0.0.8

This commit is contained in:
Gabe Farrell 2025-06-16 21:55:39 -04:00
parent 00e7782be2
commit 80b6f4deaa
66 changed files with 1559 additions and 916 deletions

View file

@ -18,70 +18,62 @@ func LoginHandler(store db.DB) http.HandlerFunc {
ctx := r.Context()
l := logger.FromContext(ctx)
l.Debug().Msg("LoginHandler: Received login request")
l.Debug().Msg("LoginHandler: Received request")
err := r.ParseForm()
if err != nil {
l.Debug().Msg("LoginHandler: Failed to parse request form")
utils.WriteError(w, "failed to parse request", http.StatusInternalServerError)
if err := r.ParseForm(); err != nil {
l.Debug().AnErr("error", err).Msg("LoginHandler: Failed to parse form")
utils.WriteError(w, "invalid request format", http.StatusBadRequest)
return
}
username := r.FormValue("username")
password := r.FormValue("password")
if username == "" || password == "" {
l.Debug().Msg("LoginHandler: Missing username or password")
utils.WriteError(w, "username and password are required", http.StatusBadRequest)
l.Debug().Msg("LoginHandler: Missing credentials")
utils.WriteError(w, "username and password required", http.StatusBadRequest)
return
}
l.Debug().Msgf("LoginHandler: Searching for user with username '%s'", username)
user, err := store.GetUserByUsername(ctx, username)
if err != nil {
l.Err(err).Msg("LoginHandler: Error searching for user in database")
utils.WriteError(w, "internal server error", http.StatusInternalServerError)
l.Error().Err(err).Msg("LoginHandler: Database error fetching user")
utils.WriteError(w, "authentication failed", http.StatusInternalServerError)
return
} else if user == nil {
l.Debug().Msg("LoginHandler: Username or password is incorrect")
utils.WriteError(w, "username or password is incorrect", http.StatusBadRequest)
}
if user == nil {
l.Debug().Msg("LoginHandler: User not found")
utils.WriteError(w, "invalid credentials", http.StatusUnauthorized)
return
}
err = bcrypt.CompareHashAndPassword(user.Password, []byte(password))
if err != nil {
l.Debug().Msg("LoginHandler: Password comparison failed")
utils.WriteError(w, "username or password is incorrect", http.StatusBadRequest)
if err := bcrypt.CompareHashAndPassword(user.Password, []byte(password)); err != nil {
l.Debug().Msg("LoginHandler: Invalid password")
utils.WriteError(w, "invalid credentials", http.StatusUnauthorized)
return
}
keepSignedIn := false
expiresAt := time.Now().Add(1 * 24 * time.Hour)
expiresAt := time.Now().Add(24 * time.Hour)
if strings.ToLower(r.FormValue("remember_me")) == "true" {
keepSignedIn = true
expiresAt = time.Now().Add(30 * 24 * time.Hour)
}
l.Debug().Msgf("LoginHandler: Creating session for user ID %d", user.ID)
session, err := store.SaveSession(ctx, user.ID, expiresAt, keepSignedIn)
session, err := store.SaveSession(ctx, user.ID, expiresAt, r.FormValue("remember_me") == "true")
if err != nil {
l.Err(err).Msg("LoginHandler: Failed to create session")
utils.WriteError(w, "failed to create session", http.StatusInternalServerError)
l.Error().Err(err).Msg("LoginHandler: Failed to create session")
utils.WriteError(w, "authentication failed", http.StatusInternalServerError)
return
}
cookie := &http.Cookie{
http.SetCookie(w, &http.Cookie{
Name: "koito_session",
Value: session.ID.String(),
Expires: expiresAt,
Path: "/",
HttpOnly: true,
Secure: false,
}
})
if keepSignedIn {
cookie.Expires = expiresAt
}
l.Debug().Msgf("LoginHandler: Session created successfully for user ID %d", user.ID)
http.SetCookie(w, cookie)
l.Debug().Msgf("LoginHandler: User %d authenticated", user.ID)
w.WriteHeader(http.StatusNoContent)
}
}
@ -91,34 +83,27 @@ func LogoutHandler(store db.DB) http.HandlerFunc {
ctx := r.Context()
l := logger.FromContext(ctx)
l.Debug().Msg("LogoutHandler: Received logout request")
l.Debug().Msg("LogoutHandler: Received request")
cookie, err := r.Cookie("koito_session")
if err == nil {
l.Debug().Msg("LogoutHandler: Found session cookie")
sid, err := uuid.Parse(cookie.Value)
if err != nil {
l.Debug().AnErr("error", err).Msg("LogoutHandler: Invalid session cookie")
utils.WriteError(w, "session cookie is invalid", http.StatusUnauthorized)
return
}
l.Debug().Msgf("LogoutHandler: Deleting session with ID %s", sid)
err = store.DeleteSession(ctx, sid)
if err != nil {
l.Err(err).Msg("LogoutHandler: Failed to delete session")
utils.WriteError(w, "internal server error", http.StatusInternalServerError)
return
l.Debug().AnErr("error", err).Msg("LogoutHandler: Invalid session ID")
} else if err := store.DeleteSession(ctx, sid); err != nil {
l.Error().Err(err).Msg("LogoutHandler: Failed to delete session")
}
}
l.Debug().Msg("LogoutHandler: Clearing session cookie")
http.SetCookie(w, &http.Cookie{
Name: "koito_session",
Value: "",
Path: "/",
HttpOnly: true,
MaxAge: -1, // expire immediately
MaxAge: -1,
})
l.Debug().Msg("LogoutHandler: Session terminated")
w.WriteHeader(http.StatusNoContent)
}
}
@ -128,16 +113,17 @@ func MeHandler(store db.DB) http.HandlerFunc {
ctx := r.Context()
l := logger.FromContext(ctx)
l.Debug().Msg("MeHandler: Received request to retrieve user information")
u := middleware.GetUserFromContext(ctx)
if u == nil {
l.Debug().Msg("MeHandler: Invalid user retrieved from context")
l.Debug().Msg("MeHandler: Received request")
user := middleware.GetUserFromContext(ctx)
if user == nil {
l.Debug().Msg("MeHandler: Unauthorized access")
utils.WriteError(w, "unauthorized", http.StatusUnauthorized)
return
}
l.Debug().Msgf("MeHandler: Successfully retrieved user with ID %d", u.ID)
utils.WriteJSON(w, http.StatusOK, u)
l.Debug().Msgf("MeHandler: Returning user data for ID %d", user.ID)
utils.WriteJSON(w, http.StatusOK, user)
}
}
@ -146,41 +132,42 @@ func UpdateUserHandler(store db.DB) http.HandlerFunc {
ctx := r.Context()
l := logger.FromContext(ctx)
l.Debug().Msg("UpdateUserHandler: Received request to update user information")
u := middleware.GetUserFromContext(ctx)
if u == nil {
l.Debug().Msg("UpdateUserHandler: Unauthorized request (user context is nil)")
l.Debug().Msg("UpdateUserHandler: Received request")
user := middleware.GetUserFromContext(ctx)
if user == nil {
l.Debug().Msg("UpdateUserHandler: Unauthorized access")
utils.WriteError(w, "unauthorized", http.StatusUnauthorized)
return
}
err := r.ParseForm()
if err != nil {
l.Err(err).Msg("UpdateUserHandler: Failed to parse request form")
utils.WriteError(w, "failed to parse request", http.StatusInternalServerError)
return
}
username := r.FormValue("username")
password := r.FormValue("password")
if username == "" && password == "" {
l.Debug().Msg("UpdateUserHandler: No parameters were recieved")
utils.WriteError(w, "all parameters missing", http.StatusBadRequest)
return
}
l.Debug().Msgf("UpdateUserHandler: Updating user with ID %d", u.ID)
err = store.UpdateUser(ctx, db.UpdateUserOpts{
ID: u.ID,
Username: username,
Password: password,
})
if err != nil {
l.Err(err).Msg("UpdateUserHandler: Failed to update user")
utils.WriteError(w, err.Error(), http.StatusBadRequest)
if err := r.ParseForm(); err != nil {
l.Error().Err(err).Msg("UpdateUserHandler: Invalid form data")
utils.WriteError(w, "invalid request", http.StatusBadRequest)
return
}
l.Debug().Msgf("UpdateUserHandler: Successfully updated user with ID %d", u.ID)
opts := db.UpdateUserOpts{ID: user.ID}
if username := r.FormValue("username"); username != "" {
opts.Username = username
}
if password := r.FormValue("password"); password != "" {
opts.Password = password
}
if opts.Username == "" && opts.Password == "" {
l.Debug().Msg("UpdateUserHandler: No update parameters provided")
utils.WriteError(w, "no changes specified", http.StatusBadRequest)
return
}
if err := store.UpdateUser(ctx, opts); err != nil {
l.Error().Err(err).Msg("UpdateUserHandler: Update failed")
utils.WriteError(w, "update failed", http.StatusBadRequest)
return
}
l.Debug().Msgf("UpdateUserHandler: User %d updated", user.ID)
w.WriteHeader(http.StatusNoContent)
}
}