librapi/forms/forms.go

248 lines
5.1 KiB
Go

package forms
import (
"errors"
"librapi/services"
"mime/multipart"
"net/http"
"strconv"
"strings"
"github.com/rs/zerolog/log"
)
const MaxFileSize = 200 // in MB
var (
ErrInvalidUsername = errors.New("username must not be empty")
ErrInvalidPassword = errors.New("password must not be empty")
ErrInvalidCredentials = errors.New("bad credentials")
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 FormFieldType interface {
int | string | StrList | UploadFile
}
type FormField[T FormFieldType] struct {
Name string
Value T
Err string
}
type UploadFile struct {
File multipart.File
Header *multipart.FileHeader
}
func (uf *UploadFile) GetFilename() string {
return uf.Header.Filename
}
func (uf *UploadFile) CheckSize() error {
if uf.Header.Size > (MaxFileSize << 20) {
return ErrFileMaxSizeReached
}
return nil
}
type UploadForm struct {
Name FormField[string]
Description FormField[string]
Editor FormField[string]
Authors FormField[StrList]
Year FormField[int]
Keywords FormField[StrList]
File FormField[UploadFile]
Error string
Method string
}
func UploadFormFromRequest(r *http.Request) UploadForm {
uf := NewUploadForm()
name := r.FormValue(uf.Name.Name)
if name == "" {
uf.Name.Err = ErrInvalidName.Error()
}
uf.Name.Value = name
uf.Description.Value = r.FormValue(uf.Description.Name)
editor := r.FormValue(uf.Editor.Name)
if editor == "" {
uf.Editor.Err = ErrInvalidEditor.Error()
}
uf.Editor.Value = editor
if a := r.FormValue(uf.Authors.Name); a != "" {
uf.Authors.Value = strings.Split(a, ",")
} else {
uf.Authors.Err = ErrInvalidAuthors.Error()
}
year, errParse := strconv.Atoi(r.FormValue(uf.Year.Name))
if errParse != nil {
log.Err(errParse).Msg("unable to parse date")
uf.Year.Err = ErrInvalidYear.Error()
}
uf.Year.Value = year
if kw := r.FormValue(uf.Keywords.Name); kw != "" {
uf.Keywords.Value = strings.Split(kw, ",")
}
file, fileh, err := r.FormFile(uf.File.Name)
if err != nil {
log.Err(err).Msg("unable to get file from form")
uf.File.Err = ErrFileOpen.Error()
return uf
}
uf.File.Value = UploadFile{
File: file,
Header: fileh,
}
if err := uf.File.Value.CheckSize(); err != nil {
uf.File.Err = err.Error()
}
return uf
}
func NewUploadForm() UploadForm {
return UploadForm{
Name: FormField[string]{
Name: "name",
},
Description: FormField[string]{
Name: "description",
},
Editor: FormField[string]{
Name: "editor",
},
Authors: FormField[StrList]{
Name: "authors",
},
Year: FormField[int]{
Name: "year",
},
Keywords: FormField[StrList]{
Name: "keywords",
},
File: FormField[UploadFile]{
Name: "file",
},
Method: http.MethodPost,
}
}
func (uf *UploadForm) HasErrors() bool {
return uf.Name.Err != "" ||
uf.Authors.Err != "" ||
uf.Editor.Err != "" ||
uf.Year.Err != "" ||
uf.Keywords.Err != "" ||
uf.File.Err != ""
}
func (uf *UploadForm) IsSuccess() bool {
return uf.Method == http.MethodPost && uf.Error == "" && !uf.HasErrors()
}
func (uf *UploadForm) IntoMetadata() *services.BookMetadata {
bm := &services.BookMetadata{
Name: uf.Name.Value,
Editor: uf.Editor.Value,
Authors: uf.Authors.Value,
Year: uint16(uf.Year.Value),
Keywords: nil,
}
if desc := uf.Description.Value; desc != "" {
bm.Description = &desc
}
if keywords := uf.Keywords.Value; len(keywords) > 0 {
bm.Keywords = keywords
}
return bm
}
type LoginForm struct {
Username FormField[string]
Password FormField[string]
Error error
Method string
}
func NewLoginForm() LoginForm {
return LoginForm{
Username: FormField[string]{
Name: "username",
},
Password: FormField[string]{
Name: "password",
},
Method: http.MethodPost,
}
}
func LoginFormFromRequest(r *http.Request) LoginForm {
lf := NewLoginForm()
username := r.FormValue(lf.Username.Name)
if username == "" {
lf.Username.Err = ErrInvalidUsername.Error()
}
lf.Username.Value = username
password := r.FormValue(lf.Password.Name)
if password == "" {
lf.Password.Err = ErrInvalidPassword.Error()
}
lf.Password.Value = password
return lf
}
func (lf *LoginForm) HasErrors() bool {
return lf.Username.Err != "" || lf.Password.Err != ""
}
func (lf *LoginForm) IsSuccess() bool {
return lf.Method == http.MethodPost && lf.Error != nil && !lf.HasErrors()
}
type SearchForm struct {
Search FormField[string]
Error error
Method string
Results []services.BookMetadata
}
func SearchFormFromRequest(r *http.Request) SearchForm {
sf := NewSearchForm()
sf.Search.Value = r.FormValue(sf.Search.Name)
return sf
}
func NewSearchForm() SearchForm {
return SearchForm{
Search: FormField[string]{
Name: "search",
},
Method: http.MethodPost,
}
}