package api import ( "errors" "log/slog" "net/http" "github.com/go-chi/chi/v5" "github.com/go-chi/render" "github.com/google/uuid" ) func Whoami(w http.ResponseWriter, r *http.Request) { slog.Debug("user: entering Whoami handler") user, ok := r.Context().Value(userKey{}).(*User) if !ok || user == nil { slog.Debug("user: anonymous user") w.Write([]byte("anonymous")) return } } func ListUsers(w http.ResponseWriter, r *http.Request) { slog.Debug("user: entering ListUsers handler") dbUsers, err := dbGetAllUsers() if err != nil { slog.Error("user: failed to fetch users", "error", err) render.Render(w, r, ErrInternal(err)) return } slog.Debug("user: successfully fetched users", "count", len(dbUsers)) if err := render.RenderList(w, r, NewUserListResponse(dbUsers)); err != nil { slog.Error("user: failed to render user list response", "error", err) render.Render(w, r, ErrInternal(err)) return } } func GetUser(w http.ResponseWriter, r *http.Request) { slog.Debug("user: entering GetUser handler") userID := chi.URLParam(r, "userID") parsed, err := uuid.Parse(userID) if err != nil { render.Render(w, r, ErrInvalidRequest(err)) return } user, err := dbGetUser(parsed.String()) if err != nil { if errors.Is(err, ErrUserNotFound) { render.Render(w, r, ErrNotFound) } else { slog.Error("user: failed to fetch user", "userid", parsed.String(), "error", err) render.Render(w, r, ErrInternal(err)) } return } slog.Debug("user: rendering user", "userid", user.ID, "username", user.Name) if err := render.Render(w, r, NewUserPayloadResponse(user)); err != nil { slog.Error("user: failed to render user", "userid", parsed.String(), "error", err) render.Render(w, r, ErrInternal(err)) } } func newUserID() uuid.UUID { return uuid.New() } func NewUser(w http.ResponseWriter, r *http.Request) { slog.Debug("user: entering NewUser handler") err := r.ParseMultipartForm(64 << 10) if err != nil { slog.Error("user: failed to parse multipartform", "error", err) http.Error(w, "Unable to parse form", http.StatusBadRequest) return } newUserName := r.FormValue("name") password := r.FormValue("password") if newUserName == "" || password == "" { slog.Error("user: username or password is empty") http.Error(w, "Username and password cannot be empty", http.StatusBadRequest) return } slog.Debug("user: hashing password for new user", "userName", newUserName) hashedPassword, err := hashPassword(password) if err != nil { slog.Error("user: failed to hash password", "error", err) http.Error(w, "Unable to hash password", http.StatusInternalServerError) return } newUser := User{ ID: newUserID(), Name: newUserName, Password: hashedPassword, } slog.Debug("user: adding new user to database", "userID", newUser.ID, "userName", newUser.Name) err = dbAddUser(&newUser) if err != nil { slog.Error("user: failed to add new user", "userID", newUser.ID, "userName", newUser.Name, "error", err) render.Render(w, r, ErrInternal(err)) return } slog.Debug("user: successfully added new user", "userID", newUser.ID, "userName", newUser.Name) render.Render(w, r, NewUserPayloadResponse(&newUser)) } type User struct { ID uuid.UUID `json:"id"` Name string `json:"name"` Password string `json:"-"` } type userKey struct{} type UserPayload struct { *User }