Skip to content

Commit e6a8de7

Browse files
authored
Allow custom placeholders (#464)
1 parent a3db8bd commit e6a8de7

14 files changed

+274
-46
lines changed

.idea/inspectionProfiles/Project_Default.xml

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/app/setup.go

+39
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/briandowns/spinner"
1010
"github.com/disintegration/imaging"
1111
"github.com/gen2brain/webp"
12+
"github.com/reaper47/recipya/web"
1213
"image/jpeg"
1314
"io"
1415
"net"
@@ -97,6 +98,44 @@ func moveFileStructure() {
9798
if count == len(dirs) {
9899
_ = os.RemoveAll(filepath.Join(exeDir, "data"))
99100
}
101+
102+
// Move placeholder.webp to images dir
103+
placeholdersDir := filepath.Join(ImagesDir, "Placeholders")
104+
err = os.MkdirAll(placeholdersDir, os.ModePerm)
105+
if err != nil {
106+
panic(err)
107+
}
108+
109+
_, err = os.Stat(filepath.Join(placeholdersDir, "placeholder.recipe.webp"))
110+
if os.IsNotExist(err) {
111+
openFile, err := web.StaticFS.Open("static/img/recipes/placeholder.webp")
112+
if err != nil {
113+
return
114+
}
115+
defer openFile.Close()
116+
117+
fRecipe, err := os.Create(filepath.Join(placeholdersDir, "placeholder.recipe.webp"))
118+
if err != nil {
119+
panic(err)
120+
}
121+
defer fRecipe.Close()
122+
123+
io.Copy(fRecipe, openFile)
124+
125+
openFileCookbook, err := web.StaticFS.Open("static/img/cookbooks-new/placeholder.webp")
126+
if err != nil {
127+
panic(err)
128+
}
129+
defer openFileCookbook.Close()
130+
131+
fCookbook, err := os.Create(filepath.Join(placeholdersDir, "placeholder.cookbook.webp"))
132+
if err != nil {
133+
panic(err)
134+
}
135+
defer fCookbook.Close()
136+
137+
io.Copy(fCookbook, openFileCookbook)
138+
}
100139
}
101140

102141
func moveFiles(srcDir, destDir string) error {

internal/server/handlers_cookbooks_test.go

+16-16
Large diffs are not rendered by default.

internal/server/handlers_general.go

+109
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package server
22

33
import (
44
"errors"
5+
"github.com/reaper47/recipya/web"
56
"io"
67
"log/slog"
78
"net/http"
@@ -80,6 +81,114 @@ func notFoundHandler(w http.ResponseWriter, r *http.Request) {
8081
_ = components.SimplePage("Page Not Found", "The page you requested to view is not found. Please go back to the main page.").Render(r.Context(), w)
8182
}
8283

84+
func (s *Server) placeholderPostHandler() http.Handler {
85+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
86+
userID := getUserID(r)
87+
userIDAttr := slog.Int64("userID", userID)
88+
89+
name := r.FormValue("name")
90+
if name != "recipe" && name != "cookbook" {
91+
s.Brokers.SendToast(models.NewErrorFormToast("Only the recipe or cookbook placeholder can be updated."), userID)
92+
w.WriteHeader(http.StatusBadRequest)
93+
return
94+
}
95+
96+
r.Body = http.MaxBytesReader(w, r.Body, 1<<24)
97+
98+
err := r.ParseMultipartForm(1 << 24)
99+
if err != nil {
100+
msg := "Could not parse the form."
101+
slog.Error(msg, userIDAttr, "error", err)
102+
s.Brokers.SendToast(models.NewErrorFormToast(msg), userID)
103+
w.WriteHeader(http.StatusBadRequest)
104+
return
105+
}
106+
107+
images, ok := r.MultipartForm.File["images"]
108+
if !ok {
109+
msg := "Could not retrieve the image from the form."
110+
slog.Error(msg, userIDAttr, "error", err)
111+
s.Brokers.SendToast(models.NewErrorFormToast(msg), userID)
112+
w.WriteHeader(http.StatusBadRequest)
113+
return
114+
}
115+
116+
if len(images) == 0 {
117+
msg := "No image has been uploaded."
118+
slog.Error(msg, userIDAttr, "error", err)
119+
s.Brokers.SendToast(models.NewErrorFormToast(msg), userID)
120+
w.WriteHeader(http.StatusBadRequest)
121+
return
122+
}
123+
124+
f, err := images[0].Open()
125+
if err != nil {
126+
msg := "Could not open the image from the form."
127+
slog.Error(msg, "error", err, userIDAttr)
128+
s.Brokers.SendToast(models.NewErrorFormToast(msg), userID)
129+
w.WriteHeader(http.StatusBadRequest)
130+
return
131+
}
132+
defer f.Close()
133+
134+
imageUUID, err := s.Files.UploadImage(f)
135+
if err != nil {
136+
msg := "Error uploading image."
137+
slog.Error(msg, "error", err, userIDAttr)
138+
s.Brokers.SendToast(models.NewErrorFilesToast(msg), userID)
139+
w.WriteHeader(http.StatusInternalServerError)
140+
return
141+
}
142+
imageUUIDAttr := slog.String("imageUUID", imageUUID.String())
143+
144+
from := filepath.Join(app.ImagesDir, imageUUID.String()+app.ImageExt)
145+
to := filepath.Join(app.ImagesDir, "Placeholders", "placeholder."+name+app.ImageExt)
146+
err = os.Rename(from, to)
147+
if err != nil {
148+
msg := "Error moving compressed placeholder image."
149+
slog.Error(msg, "error", err, imageUUIDAttr, userIDAttr)
150+
s.Brokers.SendToast(models.NewErrorFilesToast(msg), userID)
151+
w.WriteHeader(http.StatusInternalServerError)
152+
return
153+
}
154+
})
155+
}
156+
157+
func (s *Server) restorePlaceholderPostHandler() http.HandlerFunc {
158+
return func(w http.ResponseWriter, r *http.Request) {
159+
name := "recipe"
160+
path := "static/img/recipes/placeholder.webp"
161+
if r.FormValue("name") == "cookbook" {
162+
name = "cookbook"
163+
path = "static/img/cookbooks-new/placeholder.webp"
164+
}
165+
166+
userID := getUserID(r)
167+
userIDAttr := slog.Int64("userID", userID)
168+
169+
openFile, err := web.StaticFS.Open(path)
170+
if err != nil {
171+
msg := "Error opening the public file."
172+
s.Brokers.SendToast(models.NewErrorGeneralToast(msg), userID)
173+
slog.Error(msg, "error", err, userIDAttr)
174+
return
175+
}
176+
defer openFile.Close()
177+
178+
f, err := os.Create(filepath.Join(app.ImagesDir, "Placeholders", "placeholder."+name+".webp"))
179+
if err != nil {
180+
msg := "Error creating placeholder file."
181+
s.Brokers.SendToast(models.NewErrorGeneralToast(msg), userID)
182+
slog.Error(msg, "error", err, userIDAttr)
183+
w.WriteHeader(http.StatusInternalServerError)
184+
return
185+
}
186+
defer f.Close()
187+
188+
io.Copy(f, openFile)
189+
}
190+
}
191+
83192
func (s *Server) userInitialsHandler() http.HandlerFunc {
84193
return func(w http.ResponseWriter, r *http.Request) {
85194
_, _ = w.Write([]byte(s.Repository.UserInitials(getUserID(r))))

internal/server/handlers_general_test.go

+10
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,16 @@ func TestHandlers_General_Index(t *testing.T) {
155155
})
156156
}
157157

158+
func TestHandlers_General_Placeholder(t *testing.T) {
159+
srv, ts, c := createWSServer()
160+
defer c.CloseNow()
161+
162+
t.Run("must be logged in", func(t *testing.T) {
163+
assertMustBeLoggedIn(t, srv, http.MethodPost, ts.URL+"/placeholder")
164+
assertMustBeLoggedIn(t, srv, http.MethodPost, ts.URL+"/placeholder/restore")
165+
})
166+
}
167+
158168
func TestHandlers_General_Update(t *testing.T) {
159169
srv, ts, c := createWSServer()
160170
defer c.CloseNow()

0 commit comments

Comments
 (0)