...

Source file src/codeberg.org/tslocum/sriracha/internal/server/server_banner.go

Documentation: codeberg.org/tslocum/sriracha/internal/server

     1  package server
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"log"
     7  	"net/http"
     8  	"os"
     9  	"path/filepath"
    10  	"strconv"
    11  	"strings"
    12  	"time"
    13  
    14  	"codeberg.org/tslocum/sriracha/internal/database"
    15  	. "codeberg.org/tslocum/sriracha/model"
    16  	. "codeberg.org/tslocum/sriracha/util"
    17  	"github.com/gabriel-vasile/mimetype"
    18  )
    19  
    20  func (s *Server) loadBannerFormFile(db *database.DB, r *http.Request, b *Banner) ([]byte, error) {
    21  	if r.PostForm == nil {
    22  		const maxMemory = 32 << 20 // 32 MB
    23  		err := r.ParseMultipartForm(maxMemory)
    24  		if err != nil {
    25  			return nil, err
    26  		}
    27  	}
    28  	if r.MultipartForm == nil || r.MultipartForm.File == nil {
    29  		return nil, nil
    30  	}
    31  	files := r.MultipartForm.File["file"]
    32  	if len(files) == 0 {
    33  		return nil, nil
    34  	} else if len(files) > 1 {
    35  		return nil, fmt.Errorf("too many files: upload a single file")
    36  	}
    37  	fileHeader := files[0]
    38  
    39  	formFile, err := fileHeader.Open()
    40  	if err != nil {
    41  		return nil, err
    42  	}
    43  	defer formFile.Close()
    44  
    45  	buf, err := io.ReadAll(formFile)
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  
    50  	if b.Name == "" {
    51  		b.Name = strings.TrimSpace(fileHeader.Filename)
    52  		if b.Name == "" {
    53  			b.Name = fmt.Sprintf("%d", time.Now().Unix())
    54  		}
    55  	}
    56  
    57  	mimeType := mimetype.Detect(buf).String()
    58  	ext := MIMEToExt(mimeType)
    59  	if ext == "" {
    60  		return nil, fmt.Errorf("unknown file type: upload a JPG, PNG or GIF image")
    61  	}
    62  	fileExt := "." + ext
    63  	if !strings.HasSuffix(b.Name, fileExt) {
    64  		b.Name = strings.TrimSuffix(b.Name, filepath.Ext(b.Name)) + fileExt
    65  	}
    66  
    67  	imgWidth, imgHeight := s.imageDimensions(buf)
    68  	if imgWidth == 0 || imgHeight == 0 {
    69  		return nil, fmt.Errorf("invalid image")
    70  	}
    71  	b.Width, b.Height = imgWidth, imgHeight
    72  
    73  	return buf, nil
    74  }
    75  
    76  func (s *Server) loadBannerForm(db *database.DB, r *http.Request, b *Banner) {
    77  	b.Name = FormString(r, "name")
    78  	b.Overboard = FormBool(r, "overboard")
    79  	b.News = FormBool(r, "news")
    80  	b.Pages = FormBool(r, "pages")
    81  
    82  	b.Boards = nil
    83  	boards := r.Form["boards"]
    84  	for _, board := range boards {
    85  		boardID, err := strconv.Atoi(board)
    86  		if err != nil || boardID <= 0 {
    87  			continue
    88  		}
    89  		board := db.BoardByID(boardID)
    90  		if board == nil {
    91  			continue
    92  		}
    93  		b.Boards = append(b.Boards, board)
    94  	}
    95  }
    96  
    97  func (s *Server) serveBanner(data *templateData, db *database.DB, w http.ResponseWriter, r *http.Request) {
    98  	var err error
    99  	data.Template = "manage_banner"
   100  	data.Boards = db.AllBoards()
   101  	data.Manage.Banner = &Banner{}
   102  
   103  	deleteBannerID := PathInt(r, "/sriracha/banner/delete/")
   104  	if deleteBannerID > 0 {
   105  		if s.forbidden(w, data, "banner.delete") {
   106  			return
   107  		}
   108  		b := db.BannerByID(deleteBannerID)
   109  		if b == nil {
   110  			data.ManageError("Invalid banner.")
   111  			return
   112  		}
   113  
   114  		bannerPath := filepath.Join(s.config.Root, "banner", b.Name)
   115  		os.Remove(bannerPath)
   116  
   117  		db.DeleteBanner(b.ID)
   118  		s.refreshBannerCache(db)
   119  		s.rebuildAll(db)
   120  
   121  		s.log(db, data.Account, nil, fmt.Sprintf("Deleted banner #%d", b.ID), "")
   122  
   123  		http.Redirect(w, r, "/sriracha/banner/", http.StatusFound)
   124  		return
   125  	}
   126  
   127  	bannerID, err := strconv.Atoi(strings.TrimPrefix(r.URL.Path, "/sriracha/banner/"))
   128  	if err == nil && bannerID > 0 {
   129  		if s.forbidden(w, data, "banner.update") {
   130  			return
   131  		}
   132  		data.Manage.Banner = db.BannerByID(bannerID)
   133  
   134  		if data.Manage.Banner != nil && r.Method == http.MethodPost {
   135  			oldName := data.Manage.Banner.Name
   136  			s.loadBannerForm(db, r, data.Manage.Banner)
   137  			buf, err := s.loadBannerFormFile(db, r, data.Manage.Banner)
   138  			if err != nil {
   139  				data.ManageError(err.Error())
   140  				return
   141  			}
   142  
   143  			err = data.Manage.Banner.Validate()
   144  			if err != nil {
   145  				data.ManageError(err.Error())
   146  				return
   147  			}
   148  
   149  			if data.Manage.Banner.Name != oldName {
   150  				if buf == nil && filepath.Ext(data.Manage.Banner.Name) != filepath.Ext(oldName) {
   151  					data.ManageError("File extension was modified in banner name: to change a banner's file extension, upload a new file")
   152  					return
   153  				}
   154  				match := db.BannerByName(data.Manage.Banner.Name)
   155  				if match != nil {
   156  					data.ManageError("Banner with that name already exists")
   157  					return
   158  				}
   159  				oldPath := filepath.Join(s.config.Root, "banner", oldName)
   160  				newPath := filepath.Join(s.config.Root, "banner", data.Manage.Banner.Name)
   161  				err = os.Rename(oldPath, newPath)
   162  				if err != nil {
   163  					log.Fatalf("failed to rename banner from %s to %s: %s", oldPath, newPath, err)
   164  				}
   165  			}
   166  
   167  			if buf != nil {
   168  				bannerPath := filepath.Join(s.config.Root, "banner", data.Manage.Banner.Name)
   169  				err = os.WriteFile(bannerPath, buf, NewFilePermission)
   170  				if err != nil {
   171  					log.Fatalf("failed to write banner image at %s: %s", bannerPath, err)
   172  				}
   173  			}
   174  
   175  			db.UpdateBanner(data.Manage.Banner)
   176  			s.refreshBannerCache(db)
   177  			s.rebuildAll(db)
   178  
   179  			s.log(db, data.Account, nil, fmt.Sprintf("Updated >>/banner/%d", data.Manage.Banner.ID), "")
   180  
   181  			http.Redirect(w, r, "/sriracha/banner/", http.StatusFound)
   182  			return
   183  		}
   184  		return
   185  	}
   186  
   187  	if r.Method == http.MethodPost {
   188  		if s.forbidden(w, data, "banner.add") {
   189  			return
   190  		}
   191  		b := &Banner{}
   192  		s.loadBannerForm(db, r, b)
   193  		buf, err := s.loadBannerFormFile(db, r, b)
   194  		if err != nil {
   195  			data.ManageError(err.Error())
   196  			return
   197  		} else if buf == nil {
   198  			data.ManageError("upload a file to add a banner")
   199  			return
   200  		}
   201  
   202  		err = b.Validate()
   203  		if err != nil {
   204  			data.ManageError(err.Error())
   205  			return
   206  		}
   207  
   208  		match := db.BannerByName(b.Name)
   209  		if match != nil {
   210  			data.ManageError("Banner with that name already exists")
   211  			return
   212  		}
   213  
   214  		bannerPath := filepath.Join(s.config.Root, "banner", b.Name)
   215  		err = os.WriteFile(bannerPath, buf, NewFilePermission)
   216  		if err != nil {
   217  			log.Fatalf("failed to write banner image at %s: %s", bannerPath, err)
   218  		}
   219  
   220  		db.AddBanner(b)
   221  		s.refreshBannerCache(db)
   222  		s.rebuildAll(db)
   223  
   224  		s.log(db, data.Account, nil, fmt.Sprintf("Added >>/banner/%d", b.ID), "")
   225  
   226  		http.Redirect(w, r, "/sriracha/banner/", http.StatusFound)
   227  		return
   228  	}
   229  
   230  	data.Manage.Banners = db.AllBanners()
   231  }
   232  

View as plain text