You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

75 lines
2.5 KiB
Go

package server
import (
"log/slog"
"net/http"
"runtime/debug"
"strconv"
"strings"
)
// serverError helper writes a log entry at Error level (including the request
// method and URI as attributes), then sends a generic 500 Internal Server Error
// response to the user.
func serverError(w http.ResponseWriter, r *http.Request, err error) {
logger := slog.Default()
var (
method = r.Method
uri = r.URL.RequestURI()
// Use debug.Stack() to get the stack trace. This returns a byte slice, which
// we need to convert to a string so that it's readable in the log entry.
trace = string(debug.Stack())
)
logger.Error(err.Error(), "method", method, "uri", uri, "trace", trace)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
// clientError helper sends a specific status code and corresponding description
// to the user. We'll use this later in the book to send responses like 400 "Bad
// Request" when there's a problem with the request that the user sent
func clientError(w http.ResponseWriter, status int) {
http.Error(w, http.StatusText(status), status)
}
func getClientIP(r *http.Request) string {
// Check X-Forwarded-For header first (for proxied requests)
forwardedFor := r.Header.Get("X-Forwarded-For")
if forwardedFor != "" {
// Take the first IP in case of multiple proxies
return strings.Split(forwardedFor, ",")[0]
}
// Fall back to RemoteAddr
return strings.Split(r.RemoteAddr, ":")[0]
}
// TODO we probably want to distinguish between invalid email and in valid password
func logAuthFailure(logger *slog.Logger, r *http.Request, email string) {
logger.Info("authentication attempt failed",
slog.String("event_type", "authentication_failure"),
slog.String("username", email),
slog.String("ip_address", getClientIP(r)),
slog.String("user_agent", r.Header.Get("User-Agent")))
}
func logAuthSuccess(logger *slog.Logger, r *http.Request, email string, userId int) {
logger.Info("successful login",
slog.String("event_type", "authentication_success"),
slog.String("username", email),
slog.String("user_id", strconv.Itoa(userId)),
slog.String("ip_address", getClientIP(r)),
slog.String("user_agent", r.Header.Get("User-Agent")))
}
// is Authenticated returns true if an authenticated user ID has been set in the session
func isAuthenticated(r *http.Request) bool {
// return sm.Exists(r.Context(), "authenticatedUserID")
isAuthenticated, ok := r.Context().Value(isAuthenticatedKey).(bool)
if !ok {
return false
}
return isAuthenticated
}