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 }