245 lines
5.8 KiB
Go
245 lines
5.8 KiB
Go
package upload
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"mime/multipart"
|
|
"net/http"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/rs/zerolog/log"
|
|
|
|
"librapi/services"
|
|
"librapi/templates"
|
|
)
|
|
|
|
const MaxFileSize = 200 // in MB
|
|
|
|
var (
|
|
ErrInvalidName = errors.New("book name must not be empty")
|
|
ErrInvalidEditor = errors.New("book editor must not be empty")
|
|
ErrInvalidYear = errors.New("invalid year, unable to parse")
|
|
ErrInvalidAuthors = errors.New("must at least contains one author")
|
|
ErrFileMaxSizeReached = errors.New("max file size reached, must be <= 200MB")
|
|
ErrFileOpen = errors.New("unable to open file from form")
|
|
)
|
|
|
|
type StrList = []string
|
|
|
|
type BookFile struct {
|
|
file multipart.File
|
|
Header *multipart.FileHeader
|
|
}
|
|
|
|
func (bf *BookFile) GetFilename() string {
|
|
return bf.Header.Filename
|
|
}
|
|
|
|
func (bf *BookFile) CheckSize() error {
|
|
if bf.Header.Size > (MaxFileSize << 20) {
|
|
return ErrFileMaxSizeReached
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type BookFieldType interface {
|
|
int | string | StrList | BookFile
|
|
}
|
|
|
|
type BookField[T BookFieldType] struct {
|
|
Name string
|
|
Value T
|
|
Err string
|
|
}
|
|
|
|
type BookForm struct {
|
|
Name BookField[string]
|
|
Editor BookField[string]
|
|
Authors BookField[StrList]
|
|
Year BookField[int]
|
|
Keywords BookField[StrList]
|
|
File BookField[BookFile]
|
|
Error string
|
|
Method string
|
|
}
|
|
|
|
func NewBookForm() BookForm {
|
|
return BookForm{
|
|
Name: BookField[string]{
|
|
Name: "name",
|
|
},
|
|
Editor: BookField[string]{
|
|
Name: "editor",
|
|
},
|
|
Authors: BookField[StrList]{
|
|
Name: "authors",
|
|
},
|
|
Year: BookField[int]{
|
|
Name: "year",
|
|
},
|
|
Keywords: BookField[StrList]{
|
|
Name: "keywords",
|
|
},
|
|
File: BookField[BookFile]{
|
|
Name: "file",
|
|
},
|
|
Method: http.MethodPost,
|
|
}
|
|
}
|
|
|
|
func (bf *BookForm) HasErrors() bool {
|
|
return bf.Name.Err != "" || bf.Authors.Err != "" || bf.Editor.Err != "" || bf.Year.Err != "" || bf.Keywords.Err != "" || bf.File.Err != ""
|
|
}
|
|
|
|
func (bf *BookForm) IsSuccess() bool {
|
|
return bf.Method == http.MethodPost && bf.Error == "" && !bf.HasErrors()
|
|
}
|
|
|
|
func Handler(a services.IAuthenticate) func(http.ResponseWriter, *http.Request) {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
switch r.Method {
|
|
case http.MethodGet:
|
|
getUploadFile(w, r)
|
|
case http.MethodPost:
|
|
postUploadFile(w, r, a)
|
|
default:
|
|
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
|
}
|
|
}
|
|
}
|
|
|
|
func extractBookForm(r *http.Request) BookForm {
|
|
bf := NewBookForm()
|
|
|
|
name := r.FormValue(bf.Name.Name)
|
|
if name == "" {
|
|
bf.Name.Err = ErrInvalidName.Error()
|
|
}
|
|
bf.Name.Value = name
|
|
|
|
editor := r.FormValue(bf.Editor.Name)
|
|
if editor == "" {
|
|
bf.Editor.Err = ErrInvalidEditor.Error()
|
|
}
|
|
bf.Editor.Value = editor
|
|
|
|
if a := r.FormValue(bf.Authors.Name); a != "" {
|
|
bf.Authors.Value = strings.Split(a, ",")
|
|
} else {
|
|
bf.Authors.Err = ErrInvalidAuthors.Error()
|
|
}
|
|
|
|
year, errParse := strconv.Atoi(r.FormValue(bf.Year.Name))
|
|
if errParse != nil {
|
|
log.Err(errParse).Msg("unable to parse date")
|
|
bf.Year.Err = ErrInvalidYear.Error()
|
|
}
|
|
bf.Year.Value = year
|
|
|
|
if kw := r.FormValue(bf.Keywords.Name); kw != "" {
|
|
bf.Keywords.Value = strings.Split(kw, ",")
|
|
}
|
|
|
|
file, fileh, err := r.FormFile(bf.File.Name)
|
|
if err != nil {
|
|
log.Err(err).Msg("unable to get file from form")
|
|
bf.File.Err = ErrFileOpen.Error()
|
|
return bf
|
|
}
|
|
|
|
bf.File.Value = BookFile{
|
|
file: file,
|
|
Header: fileh,
|
|
}
|
|
|
|
if err := bf.File.Value.CheckSize(); err != nil {
|
|
bf.File.Err = err.Error()
|
|
}
|
|
|
|
return bf
|
|
}
|
|
|
|
func postUploadFile(w http.ResponseWriter, r *http.Request, a services.IAuthenticate) {
|
|
uploadForm := templates.GetUploadForm()
|
|
|
|
if !a.IsLogged(r) {
|
|
buf := bytes.NewBufferString("")
|
|
if err := uploadForm.Execute(buf, &BookForm{Error: services.ErrUnauthorized.Error()}); err != nil {
|
|
log.Err(err).Msg("unable to generate template")
|
|
http.Error(w, "unexpected error occurred", http.StatusInternalServerError)
|
|
}
|
|
|
|
w.WriteHeader(http.StatusUnauthorized)
|
|
fmt.Fprint(w, buf.String())
|
|
return
|
|
}
|
|
|
|
bf := extractBookForm(r)
|
|
buf := bytes.NewBufferString("")
|
|
if err := uploadForm.Execute(buf, &bf); err != nil {
|
|
log.Err(err).Msg("unable to generate template")
|
|
http.Error(w, "unexpected error occurred", http.StatusInternalServerError)
|
|
}
|
|
|
|
if bf.HasErrors() {
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
fmt.Fprint(w, buf.String())
|
|
return
|
|
}
|
|
|
|
filename := bf.File.Value.GetFilename()
|
|
log.Info().Str("filename", filename).Msg("file is uploading...")
|
|
|
|
dst, err := os.Create(filename)
|
|
if err != nil {
|
|
if err := uploadForm.Execute(buf, &BookForm{Error: "unexpected error occurred while creating file"}); err != nil {
|
|
log.Err(err).Msg("unable to generate template")
|
|
http.Error(w, "unexpected error occurred", http.StatusInternalServerError)
|
|
}
|
|
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
fmt.Fprint(w, buf.String())
|
|
return
|
|
}
|
|
|
|
defer dst.Close()
|
|
|
|
if _, err := io.Copy(dst, bf.File.Value.file); err != nil {
|
|
if err := uploadForm.Execute(buf, &BookForm{Error: "unexpected error occurred while uploading file"}); err != nil {
|
|
log.Err(err).Msg("unable to generate template")
|
|
http.Error(w, "unexpected error occurred", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
fmt.Fprint(w, buf.String())
|
|
return
|
|
}
|
|
|
|
buf.Reset()
|
|
if err := uploadForm.Execute(buf, &BookForm{Method: http.MethodPost}); err != nil {
|
|
log.Err(err).Msg("unable to generate template")
|
|
http.Error(w, "unexpected error occurred", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
fmt.Fprint(w, buf.String())
|
|
}
|
|
|
|
func getUploadFile(w http.ResponseWriter, _ *http.Request) {
|
|
uploadForm := templates.GetUploadForm()
|
|
|
|
buf := bytes.NewBufferString("")
|
|
if err := uploadForm.Execute(buf, &BookForm{}); err != nil {
|
|
log.Err(err).Msg("unable to generate template")
|
|
http.Error(w, "unexpected error occurred", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
fmt.Fprint(w, buf)
|
|
}
|