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
+26 -1
View File
@@ -18,6 +18,7 @@ import (
"github.com/alexedwards/scs/v2"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/google/uuid"
"plumber/internal/blob"
"plumber/internal/events"
@@ -104,6 +105,11 @@ type threadPostCtx struct {
Depth int
}
type imagePickerCtx struct {
ID string
Images []store.PostImage
}
func New(st store.Store, sessionStore scs.Store, templateFS fs.FS, staticFS fs.FS, cfg Config) (*Server, error) {
if cfg.Blob == nil {
cfg.Blob = blob.Disabled{}
@@ -118,6 +124,12 @@ func New(st store.Store, sessionStore scs.Store, templateFS fs.FS, staticFS fs.F
"postCtx": func(user *store.User, csrf string, root, post *store.Post, depth int) threadPostCtx {
return threadPostCtx{User: user, CSRF: csrf, Root: root, Post: post, Depth: depth}
},
"imagePicker": func(id string, images []store.PostImage) imagePickerCtx {
return imagePickerCtx{ID: id, Images: images}
},
"newImagePicker": func(id string) imagePickerCtx {
return imagePickerCtx{ID: id}
},
"add": func(a, b int) int { return a + b },
"rank": func(i int) int { return i + 1 },
"isAdmin": func(u *store.User) bool { return u.Admin() },
@@ -183,7 +195,7 @@ func (s *Server) Handler() http.Handler {
r.Use(middleware.Recoverer)
r.Use(func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
r.Body = http.MaxBytesReader(w, r.Body, 3<<20)
r.Body = http.MaxBytesReader(w, r.Body, requestBodyLimit(r))
next.ServeHTTP(w, r)
})
})
@@ -332,6 +344,11 @@ func (s *Server) handleSubmitForm(w http.ResponseWriter, r *http.Request) {
}
func (s *Server) handleSubmit(w http.ResponseWriter, r *http.Request) {
cleanup, ok := parsePostMutationForm(w, r)
if !ok {
return
}
defer cleanup()
if !s.requireCSRF(w, r) {
return
}
@@ -363,12 +380,20 @@ func (s *Server) handleSubmit(w http.ResponseWriter, r *http.Request) {
city = truncateRunes(city, 80)
}
post := &store.Post{
ID: uuid.NewString(),
AuthorID: u.ID,
Title: title,
Body: body,
City: city,
}
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)
http.Error(w, "could not save question", http.StatusInternalServerError)
return
}