Add post image upload (#20)
CI / test (push) Successful in 6m28s

Lands the image upload handlers and picker on master. Questions and replies can attach up to four JPEG, PNG, or WebP photos.

Reviewed-on: #20
Co-authored-by: codegirl-007 <s.raide@gmail.com>
This commit was merged in pull request #20.
This commit is contained in:
2026-08-31 04:29:51 +00:00
committed by codegirl007
parent e68afba6a5
commit 2eb8bff1c4
14 changed files with 1525 additions and 5 deletions
+28
View File
@@ -10,6 +10,7 @@ import (
"strings"
"github.com/go-chi/chi/v5"
"github.com/google/uuid"
"plumber/internal/store"
)
@@ -17,6 +18,11 @@ import (
// handleCreatePost creates either a root question or a reply. Replies are
// limited to the root author and admins, and cannot be added to hidden threads.
func (s *Server) handleCreatePost(w http.ResponseWriter, r *http.Request) {
cleanup, ok := parsePostMutationForm(w, r)
if !ok {
return
}
defer cleanup()
if !s.requireCSRF(w, r) {
return
}
@@ -34,6 +40,7 @@ func (s *Server) handleCreatePost(w http.ResponseWriter, r *http.Request) {
}
post := &store.Post{
ID: uuid.NewString(),
AuthorID: user.ID,
Body: truncateRunes(body, 12000),
}
@@ -68,7 +75,14 @@ func (s *Server) handleCreatePost(w http.ResponseWriter, r *http.Request) {
root = threadRoot
}
images, newKeys, err := s.postImagesFromForm(r.Context(), r, post.ID, nil)
if err != nil {
writePostImageRequestError(w, err)
return
}
post.Images = images
if err := s.store.CreatePost(r.Context(), post); err != nil {
s.deletePostImageObjects(newKeys)
if errors.Is(err, store.ErrInvalidPost) {
http.Error(w, "invalid post", http.StatusBadRequest)
return
@@ -91,6 +105,11 @@ func (s *Server) handleCreatePost(w http.ResponseWriter, r *http.Request) {
// handleEditPost updates only a post's body after verifying that the current
// homeowner owns it or that an admin is editing an admin-authored post.
func (s *Server) handleEditPost(w http.ResponseWriter, r *http.Request) {
cleanup, ok := parsePostMutationForm(w, r)
if !ok {
return
}
defer cleanup()
if !s.requireCSRF(w, r) {
return
}
@@ -119,8 +138,16 @@ func (s *Server) handleEditPost(w http.ResponseWriter, r *http.Request) {
http.Error(w, "post body required", http.StatusBadRequest)
return
}
previousImages := append([]store.PostImage(nil), post.Images...)
images, newKeys, err := s.postImagesFromForm(r.Context(), r, post.ID, previousImages)
if err != nil {
writePostImageRequestError(w, err)
return
}
post.Body = truncateRunes(body, 12000)
post.Images = images
if err := s.store.UpdatePost(r.Context(), post); err != nil {
s.deletePostImageObjects(newKeys)
if errors.Is(err, store.ErrInvalidPost) {
http.Error(w, "invalid post", http.StatusBadRequest)
return
@@ -132,6 +159,7 @@ func (s *Server) handleEditPost(w http.ResponseWriter, r *http.Request) {
http.Error(w, "could not save post", http.StatusInternalServerError)
return
}
s.deletePostImageObjects(removedPostImageKeys(previousImages, images))
s.publishPostUpdated(post, root, nil)
http.Redirect(
w,