294 lines
6.2 KiB
Go
294 lines
6.2 KiB
Go
package forms
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"io"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/rs/zerolog/log"
|
|
|
|
"librapi/services"
|
|
)
|
|
|
|
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("resource name must not be empty")
|
|
ErrInvalidEditor = errors.New("resource editor must not be empty")
|
|
ErrInvalidYear = errors.New("invalid year, unable to parse")
|
|
ErrInvalidYearRange = errors.New("invalid year, can't be greater than today")
|
|
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")
|
|
ErrFileUnreadable = errors.New("unable to read the file")
|
|
)
|
|
|
|
type StrList = []string
|
|
|
|
type FormFieldType interface {
|
|
int | string | StrList | UploadFile
|
|
}
|
|
|
|
type FormField[T FormFieldType] struct {
|
|
Name string
|
|
Value T
|
|
Err string
|
|
}
|
|
|
|
type UploadFile struct {
|
|
filename string
|
|
content []byte
|
|
size int64
|
|
}
|
|
|
|
func (uf *UploadFile) GetFilename() string {
|
|
return uf.filename
|
|
}
|
|
|
|
func (uf *UploadFile) CheckSize() error {
|
|
if uf.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()
|
|
}
|
|
if year > time.Now().Year() {
|
|
log.Error().Msg("bad date range")
|
|
uf.Year.Err = ErrInvalidYearRange.Error()
|
|
}
|
|
uf.Year.Value = year
|
|
|
|
if kw := r.FormValue(uf.Keywords.Name); kw != "" {
|
|
uf.Keywords.Value = strings.Split(kw, ",")
|
|
}
|
|
|
|
uf.sanitize()
|
|
|
|
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
|
|
}
|
|
defer file.Close()
|
|
|
|
content, err := io.ReadAll(file)
|
|
if err != nil {
|
|
log.Err(err).Msg("unable to get read file from form")
|
|
uf.File.Err = ErrFileOpen.Error()
|
|
return uf
|
|
}
|
|
|
|
uf.File.Value = UploadFile{
|
|
filename: fileh.Filename,
|
|
content: content,
|
|
size: fileh.Size,
|
|
}
|
|
|
|
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) IntoResource() *services.Resource {
|
|
bm := &services.Resource{
|
|
Name: uf.Name.Value,
|
|
Editor: uf.Editor.Value,
|
|
Authors: uf.Authors.Value,
|
|
Year: uf.Year.Value,
|
|
Keywords: nil,
|
|
Content: bytes.NewBuffer(uf.File.Value.content),
|
|
}
|
|
|
|
if desc := uf.Description.Value; desc != "" {
|
|
bm.Description = &desc
|
|
}
|
|
|
|
if keywords := uf.Keywords.Value; len(keywords) > 0 {
|
|
bm.Keywords = keywords
|
|
}
|
|
|
|
return bm
|
|
}
|
|
|
|
func (uf *UploadForm) sanitize() {
|
|
uf.Name.Value = strings.TrimSpace(uf.Name.Value)
|
|
uf.Editor.Value = strings.TrimSpace(uf.Editor.Value)
|
|
uf.Description.Value = strings.TrimSpace(uf.Description.Value)
|
|
|
|
authors := []string{}
|
|
for _, a := range uf.Authors.Value {
|
|
if a == "" {
|
|
continue
|
|
}
|
|
authors = append(authors, strings.TrimSpace(a))
|
|
}
|
|
uf.Authors.Value = authors
|
|
|
|
keywords := []string{}
|
|
for _, k := range uf.Keywords.Value {
|
|
if k == "" {
|
|
continue
|
|
}
|
|
keywords = append(keywords, strings.TrimSpace(k))
|
|
}
|
|
uf.Keywords.Value = keywords
|
|
}
|
|
|
|
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.Resource
|
|
}
|
|
|
|
func SearchFormFromRequest(r *http.Request) SearchForm {
|
|
sf := NewSearchForm()
|
|
sf.Search.Value = strings.TrimSpace(r.FormValue(sf.Search.Name))
|
|
return sf
|
|
}
|
|
|
|
func NewSearchForm() SearchForm {
|
|
return SearchForm{
|
|
Search: FormField[string]{
|
|
Name: "search",
|
|
},
|
|
Method: http.MethodPost,
|
|
}
|
|
}
|