package api import ( "context" "net/http" "github.com/go-chi/chi/v5" "github.com/go-chi/render" "github.com/google/uuid" ) func UserCtx(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var user *User var err error if userID := chi.URLParam(r, "userID"); userID != "" { user, err = dbGetUser(userID) } else { render.Render(w, r, ErrNotFound) return } if err != nil { render.Render(w, r, ErrNotFound) return } ctx := context.WithValue(r.Context(), userKey{}, user) next.ServeHTTP(w, r.WithContext(ctx)) }) } func Whoami(w http.ResponseWriter, r *http.Request) { user, ok := r.Context().Value(userKey{}).(*User) if !ok || user == nil { // Anonymous user w.Write([]byte("anonymous")) return } w.Write([]byte(user.Name)) } func LoginCtx(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Try to retrieve username from context username, ok := r.Context().Value(usernameKey).(string) if !ok || username == "" { // No username provided, assume it's an anonymous user next.ServeHTTP(w, r) return } // Lookup user in the database user, err := dbGetUserByName(username) if err != nil { // If user is specified and not found, throw an error render.Render(w, r, ErrNotFound) return } ctx := context.WithValue(r.Context(), userKey{}, user) next.ServeHTTP(w, r.WithContext(ctx)) }) } func GetUser(w http.ResponseWriter, r *http.Request) { user, ok := r.Context().Value(userKey{}).(*User) if !ok || user == nil { render.Render(w, r, ErrNotFound) return } if err := render.Render(w, r, NewUserPayloadResponse(user)); err != nil { render.Render(w, r, ErrRender(err)) return } } func ListUsers(w http.ResponseWriter, r *http.Request) { dbUsers, err := dbGetAllUsers() if err != nil { render.Render(w, r, ErrRender(err)) return } if err := render.RenderList(w, r, NewUserListResponse(dbUsers)); err != nil { render.Render(w, r, ErrRender(err)) return } } func newUserID() uuid.UUID { return uuid.New() } func NewUser(w http.ResponseWriter, r *http.Request) { err := r.ParseMultipartForm(64 << 10) if err != nil { http.Error(w, "Unable to parse form", http.StatusBadRequest) return } newUserName := r.FormValue("name") password := r.FormValue("password") if newUserName == "" || password == "" { http.Error(w, "Username and password cannot be empty", http.StatusBadRequest) return } hashedPassword, err := hashPassword(password) if err != nil { http.Error(w, "Unable to hash password", http.StatusInternalServerError) } newUser := User{ ID: newUserID(), Name: newUserName, Password: hashedPassword, } err = dbAddUser(&newUser) if err != nil { render.Render(w, r, ErrRender(err)) return } render.Render(w, r, NewUserPayloadResponse(&newUser)) } type userKey struct{} type User struct { ID uuid.UUID `json:"id"` Name string `json:"name"` Password string `json:"-"` } type UserPayload struct { *User }