refactor(pages): deduplicate image upload/delete handlers
Some checks failed
Build and Release / Create Release (push) Successful in 0s
Build and Release / Integration Tests (PostgreSQL) (push) Successful in 2m51s
Build and Release / Unit Tests (push) Successful in 8m56s
Build and Release / Lint (push) Successful in 9m26s
Build and Release / Build Binaries (amd64, darwin, macos) (push) Failing after 1s
Build and Release / Build Binaries (arm64, darwin, macos) (push) Failing after 0s
Build and Release / Build Binaries (amd64, linux, linux-latest) (push) Successful in 4m50s
Build and Release / Build Binaries (amd64, windows, windows-latest) (push) Successful in 9h7m28s
Build and Release / Build Binary (linux/arm64) (push) Successful in 8m1s
Some checks failed
Build and Release / Create Release (push) Successful in 0s
Build and Release / Integration Tests (PostgreSQL) (push) Successful in 2m51s
Build and Release / Unit Tests (push) Successful in 8m56s
Build and Release / Lint (push) Successful in 9m26s
Build and Release / Build Binaries (amd64, darwin, macos) (push) Failing after 1s
Build and Release / Build Binaries (arm64, darwin, macos) (push) Failing after 0s
Build and Release / Build Binaries (amd64, linux, linux-latest) (push) Successful in 4m50s
Build and Release / Build Binaries (amd64, windows, windows-latest) (push) Successful in 9h7m28s
Build and Release / Build Binary (linux/arm64) (push) Successful in 8m1s
Extract common image upload and delete logic into pagesUploadImage and pagesDeleteImage helper functions. Eliminates ~90 lines of duplicated code across logo, favicon, and hero image handlers. Uses function parameters for field-specific operations (get/set config fields, locale keys, file prefixes). Makes it easier to add new image upload fields in the future.
This commit is contained in:
@@ -261,23 +261,33 @@ func PagesBrandPost(ctx *context.Context) {
|
||||
ctx.Redirect(ctx.Repo.Repository.Link() + "/settings/pages/brand")
|
||||
}
|
||||
|
||||
const maxBrandImageSize = 5 * 1024 * 1024 // 5 MB
|
||||
const maxPagesImageSize = 5 * 1024 * 1024 // 5 MB
|
||||
|
||||
// PagesBrandUploadLogo handles logo image file upload.
|
||||
func PagesBrandUploadLogo(ctx *context.Context) {
|
||||
// pagesUploadImage is a shared helper for uploading images to pages config fields.
|
||||
// formField is the multipart form field name, filePrefix is used in the stored filename,
|
||||
// redirectPath is the settings page to redirect to, getOld/setNew read/write the config field,
|
||||
// and successKey is the locale key for the success flash message.
|
||||
func pagesUploadImage(
|
||||
ctx *context.Context,
|
||||
formField, filePrefix, redirectPath string,
|
||||
getOld func(*pages_module.LandingConfig) string,
|
||||
setNew func(*pages_module.LandingConfig, string),
|
||||
errorKey, tooLargeKey, notImageKey, successKey string,
|
||||
) {
|
||||
repo := ctx.Repo.Repository
|
||||
redirect := repo.Link() + redirectPath
|
||||
|
||||
file, header, err := ctx.Req.FormFile("brand_logo")
|
||||
file, header, err := ctx.Req.FormFile(formField)
|
||||
if err != nil {
|
||||
ctx.Flash.Error(ctx.Tr("repo.settings.pages.brand_upload_error"))
|
||||
ctx.Redirect(repo.Link() + "/settings/pages/brand")
|
||||
ctx.Flash.Error(ctx.Tr(errorKey))
|
||||
ctx.Redirect(redirect)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
if header.Size > maxBrandImageSize {
|
||||
ctx.Flash.Error(ctx.Tr("repo.settings.pages.brand_image_too_large"))
|
||||
ctx.Redirect(repo.Link() + "/settings/pages/brand")
|
||||
if header.Size > maxPagesImageSize {
|
||||
ctx.Flash.Error(ctx.Tr(tooLargeKey))
|
||||
ctx.Redirect(redirect)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -289,18 +299,18 @@ func PagesBrandUploadLogo(ctx *context.Context) {
|
||||
|
||||
st := typesniffer.DetectContentType(data)
|
||||
if !(st.IsImage() && !st.IsSvgImage()) {
|
||||
ctx.Flash.Error(ctx.Tr("repo.settings.pages.brand_not_an_image"))
|
||||
ctx.Redirect(repo.Link() + "/settings/pages/brand")
|
||||
ctx.Flash.Error(ctx.Tr(notImageKey))
|
||||
ctx.Redirect(redirect)
|
||||
return
|
||||
}
|
||||
|
||||
hash := sha256.Sum256(data)
|
||||
filename := fmt.Sprintf("pages-logo-%d-%x", repo.ID, hash[:8])
|
||||
filename := fmt.Sprintf("%s-%d-%x", filePrefix, repo.ID, hash[:8])
|
||||
|
||||
config := getPagesLandingConfig(ctx)
|
||||
|
||||
if config.Brand.UploadedLogo != "" {
|
||||
_ = storage.RepoAvatars.Delete(config.Brand.UploadedLogo)
|
||||
if old := getOld(config); old != "" {
|
||||
_ = storage.RepoAvatars.Delete(old)
|
||||
}
|
||||
|
||||
if err := storage.SaveFrom(storage.RepoAvatars, filename, func(w io.Writer) error {
|
||||
@@ -311,108 +321,72 @@ func PagesBrandUploadLogo(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
config.Brand.UploadedLogo = filename
|
||||
setNew(config, filename)
|
||||
if err := savePagesLandingConfig(ctx, config); err != nil {
|
||||
ctx.ServerError("SavePagesConfig", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Flash.Success(ctx.Tr("repo.settings.pages.brand_logo_uploaded"))
|
||||
ctx.Redirect(repo.Link() + "/settings/pages/brand")
|
||||
ctx.Flash.Success(ctx.Tr(successKey))
|
||||
ctx.Redirect(redirect)
|
||||
}
|
||||
|
||||
// pagesDeleteImage is a shared helper for deleting uploaded images from pages config fields.
|
||||
func pagesDeleteImage(
|
||||
ctx *context.Context,
|
||||
redirectPath string,
|
||||
getOld func(*pages_module.LandingConfig) string,
|
||||
clearField func(*pages_module.LandingConfig),
|
||||
successKey string,
|
||||
) {
|
||||
repo := ctx.Repo.Repository
|
||||
config := getPagesLandingConfig(ctx)
|
||||
|
||||
if old := getOld(config); old != "" {
|
||||
_ = storage.RepoAvatars.Delete(old)
|
||||
clearField(config)
|
||||
if err := savePagesLandingConfig(ctx, config); err != nil {
|
||||
ctx.ServerError("SavePagesConfig", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Flash.Success(ctx.Tr(successKey))
|
||||
ctx.Redirect(repo.Link() + redirectPath)
|
||||
}
|
||||
|
||||
// PagesBrandUploadLogo handles logo image file upload.
|
||||
func PagesBrandUploadLogo(ctx *context.Context) {
|
||||
pagesUploadImage(ctx, "brand_logo", "pages-logo", "/settings/pages/brand",
|
||||
func(c *pages_module.LandingConfig) string { return c.Brand.UploadedLogo },
|
||||
func(c *pages_module.LandingConfig, f string) { c.Brand.UploadedLogo = f },
|
||||
"repo.settings.pages.brand_upload_error", "repo.settings.pages.brand_image_too_large",
|
||||
"repo.settings.pages.brand_not_an_image", "repo.settings.pages.brand_logo_uploaded")
|
||||
}
|
||||
|
||||
// PagesBrandDeleteLogo removes the uploaded logo image.
|
||||
func PagesBrandDeleteLogo(ctx *context.Context) {
|
||||
repo := ctx.Repo.Repository
|
||||
config := getPagesLandingConfig(ctx)
|
||||
|
||||
if config.Brand.UploadedLogo != "" {
|
||||
_ = storage.RepoAvatars.Delete(config.Brand.UploadedLogo)
|
||||
config.Brand.UploadedLogo = ""
|
||||
if err := savePagesLandingConfig(ctx, config); err != nil {
|
||||
ctx.ServerError("SavePagesConfig", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Flash.Success(ctx.Tr("repo.settings.pages.brand_logo_deleted"))
|
||||
ctx.Redirect(repo.Link() + "/settings/pages/brand")
|
||||
pagesDeleteImage(ctx, "/settings/pages/brand",
|
||||
func(c *pages_module.LandingConfig) string { return c.Brand.UploadedLogo },
|
||||
func(c *pages_module.LandingConfig) { c.Brand.UploadedLogo = "" },
|
||||
"repo.settings.pages.brand_logo_deleted")
|
||||
}
|
||||
|
||||
// PagesBrandUploadFavicon handles favicon file upload.
|
||||
func PagesBrandUploadFavicon(ctx *context.Context) {
|
||||
repo := ctx.Repo.Repository
|
||||
|
||||
file, header, err := ctx.Req.FormFile("brand_favicon")
|
||||
if err != nil {
|
||||
ctx.Flash.Error(ctx.Tr("repo.settings.pages.brand_upload_error"))
|
||||
ctx.Redirect(repo.Link() + "/settings/pages/brand")
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
if header.Size > maxBrandImageSize {
|
||||
ctx.Flash.Error(ctx.Tr("repo.settings.pages.brand_image_too_large"))
|
||||
ctx.Redirect(repo.Link() + "/settings/pages/brand")
|
||||
return
|
||||
}
|
||||
|
||||
data, err := io.ReadAll(file)
|
||||
if err != nil {
|
||||
ctx.ServerError("ReadAll", err)
|
||||
return
|
||||
}
|
||||
|
||||
st := typesniffer.DetectContentType(data)
|
||||
if !(st.IsImage() && !st.IsSvgImage()) {
|
||||
ctx.Flash.Error(ctx.Tr("repo.settings.pages.brand_not_an_image"))
|
||||
ctx.Redirect(repo.Link() + "/settings/pages/brand")
|
||||
return
|
||||
}
|
||||
|
||||
hash := sha256.Sum256(data)
|
||||
filename := fmt.Sprintf("pages-favicon-%d-%x", repo.ID, hash[:8])
|
||||
|
||||
config := getPagesLandingConfig(ctx)
|
||||
|
||||
if config.Brand.UploadedFavicon != "" {
|
||||
_ = storage.RepoAvatars.Delete(config.Brand.UploadedFavicon)
|
||||
}
|
||||
|
||||
if err := storage.SaveFrom(storage.RepoAvatars, filename, func(w io.Writer) error {
|
||||
_, err := w.Write(data)
|
||||
return err
|
||||
}); err != nil {
|
||||
ctx.ServerError("SaveFrom", err)
|
||||
return
|
||||
}
|
||||
|
||||
config.Brand.UploadedFavicon = filename
|
||||
if err := savePagesLandingConfig(ctx, config); err != nil {
|
||||
ctx.ServerError("SavePagesConfig", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Flash.Success(ctx.Tr("repo.settings.pages.brand_favicon_uploaded"))
|
||||
ctx.Redirect(repo.Link() + "/settings/pages/brand")
|
||||
pagesUploadImage(ctx, "brand_favicon", "pages-favicon", "/settings/pages/brand",
|
||||
func(c *pages_module.LandingConfig) string { return c.Brand.UploadedFavicon },
|
||||
func(c *pages_module.LandingConfig, f string) { c.Brand.UploadedFavicon = f },
|
||||
"repo.settings.pages.brand_upload_error", "repo.settings.pages.brand_image_too_large",
|
||||
"repo.settings.pages.brand_not_an_image", "repo.settings.pages.brand_favicon_uploaded")
|
||||
}
|
||||
|
||||
// PagesBrandDeleteFavicon removes the uploaded favicon.
|
||||
func PagesBrandDeleteFavicon(ctx *context.Context) {
|
||||
repo := ctx.Repo.Repository
|
||||
config := getPagesLandingConfig(ctx)
|
||||
|
||||
if config.Brand.UploadedFavicon != "" {
|
||||
_ = storage.RepoAvatars.Delete(config.Brand.UploadedFavicon)
|
||||
config.Brand.UploadedFavicon = ""
|
||||
if err := savePagesLandingConfig(ctx, config); err != nil {
|
||||
ctx.ServerError("SavePagesConfig", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Flash.Success(ctx.Tr("repo.settings.pages.brand_favicon_deleted"))
|
||||
ctx.Redirect(repo.Link() + "/settings/pages/brand")
|
||||
pagesDeleteImage(ctx, "/settings/pages/brand",
|
||||
func(c *pages_module.LandingConfig) string { return c.Brand.UploadedFavicon },
|
||||
func(c *pages_module.LandingConfig) { c.Brand.UploadedFavicon = "" },
|
||||
"repo.settings.pages.brand_favicon_deleted")
|
||||
}
|
||||
|
||||
func PagesHero(ctx *context.Context) {
|
||||
@@ -444,83 +418,21 @@ func PagesHeroPost(ctx *context.Context) {
|
||||
ctx.Redirect(ctx.Repo.Repository.Link() + "/settings/pages/hero")
|
||||
}
|
||||
|
||||
const maxHeroImageSize = 5 * 1024 * 1024 // 5MB
|
||||
|
||||
// PagesHeroUploadImage handles hero image file upload.
|
||||
func PagesHeroUploadImage(ctx *context.Context) {
|
||||
repo := ctx.Repo.Repository
|
||||
|
||||
file, header, err := ctx.Req.FormFile("hero_image")
|
||||
if err != nil {
|
||||
ctx.Flash.Error(ctx.Tr("repo.settings.pages.hero_upload_error"))
|
||||
ctx.Redirect(repo.Link() + "/settings/pages/hero")
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
if header.Size > maxHeroImageSize {
|
||||
ctx.Flash.Error(ctx.Tr("repo.settings.pages.hero_image_too_large"))
|
||||
ctx.Redirect(repo.Link() + "/settings/pages/hero")
|
||||
return
|
||||
}
|
||||
|
||||
data, err := io.ReadAll(file)
|
||||
if err != nil {
|
||||
ctx.ServerError("ReadAll", err)
|
||||
return
|
||||
}
|
||||
|
||||
st := typesniffer.DetectContentType(data)
|
||||
if !(st.IsImage() && !st.IsSvgImage()) {
|
||||
ctx.Flash.Error(ctx.Tr("repo.settings.pages.hero_not_an_image"))
|
||||
ctx.Redirect(repo.Link() + "/settings/pages/hero")
|
||||
return
|
||||
}
|
||||
|
||||
hash := sha256.Sum256(data)
|
||||
filename := fmt.Sprintf("pages-hero-%d-%x", repo.ID, hash[:8])
|
||||
|
||||
config := getPagesLandingConfig(ctx)
|
||||
|
||||
// Delete old uploaded image if it exists
|
||||
if config.Hero.UploadedImage != "" {
|
||||
_ = storage.RepoAvatars.Delete(config.Hero.UploadedImage)
|
||||
}
|
||||
|
||||
if err := storage.SaveFrom(storage.RepoAvatars, filename, func(w io.Writer) error {
|
||||
_, err := w.Write(data)
|
||||
return err
|
||||
}); err != nil {
|
||||
ctx.ServerError("SaveFrom", err)
|
||||
return
|
||||
}
|
||||
|
||||
config.Hero.UploadedImage = filename
|
||||
if err := savePagesLandingConfig(ctx, config); err != nil {
|
||||
ctx.ServerError("SavePagesConfig", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Flash.Success(ctx.Tr("repo.settings.pages.hero_image_uploaded"))
|
||||
ctx.Redirect(repo.Link() + "/settings/pages/hero")
|
||||
pagesUploadImage(ctx, "hero_image", "pages-hero", "/settings/pages/hero",
|
||||
func(c *pages_module.LandingConfig) string { return c.Hero.UploadedImage },
|
||||
func(c *pages_module.LandingConfig, f string) { c.Hero.UploadedImage = f },
|
||||
"repo.settings.pages.hero_upload_error", "repo.settings.pages.hero_image_too_large",
|
||||
"repo.settings.pages.hero_not_an_image", "repo.settings.pages.hero_image_uploaded")
|
||||
}
|
||||
|
||||
// PagesHeroDeleteImage removes the uploaded hero image.
|
||||
func PagesHeroDeleteImage(ctx *context.Context) {
|
||||
repo := ctx.Repo.Repository
|
||||
config := getPagesLandingConfig(ctx)
|
||||
|
||||
if config.Hero.UploadedImage != "" {
|
||||
_ = storage.RepoAvatars.Delete(config.Hero.UploadedImage)
|
||||
config.Hero.UploadedImage = ""
|
||||
if err := savePagesLandingConfig(ctx, config); err != nil {
|
||||
ctx.ServerError("SavePagesConfig", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Flash.Success(ctx.Tr("repo.settings.pages.hero_image_deleted"))
|
||||
ctx.Redirect(repo.Link() + "/settings/pages/hero")
|
||||
pagesDeleteImage(ctx, "/settings/pages/hero",
|
||||
func(c *pages_module.LandingConfig) string { return c.Hero.UploadedImage },
|
||||
func(c *pages_module.LandingConfig) { c.Hero.UploadedImage = "" },
|
||||
"repo.settings.pages.hero_image_deleted")
|
||||
}
|
||||
|
||||
func PagesContent(ctx *context.Context) {
|
||||
|
||||
Reference in New Issue
Block a user