rework template load/generation + add generic error on template error

This commit is contained in:
rmanach
2025-01-07 16:55:36 +01:00
parent 40e08154f6
commit 708db35214
13 changed files with 501 additions and 449 deletions
+11 -1
View File
@@ -4,6 +4,7 @@ import (
"fmt"
"librapi/services"
"net/http"
"os"
"path/filepath"
"github.com/rs/zerolog/log"
@@ -27,7 +28,7 @@ func getDownload(w http.ResponseWriter, r *http.Request, bs services.IStore) {
downloadFiles, ok := queryParams["file"]
if !ok {
log.Error().Msg("file query param does not exist")
http.Error(w, "file does not exists", http.StatusBadRequest)
http.Error(w, "file query param does not exist", http.StatusBadRequest)
return
}
@@ -40,6 +41,15 @@ func getDownload(w http.ResponseWriter, r *http.Request, bs services.IStore) {
filename := downloadFiles[0]
filePath := filepath.Join(bs.GetStoreDir(), filename)
if _, err := os.Stat(filePath); err != nil {
if os.IsNotExist(err) {
http.Error(w, "file does not exist", http.StatusInternalServerError)
return
}
http.Error(w, "unexpected error occurred", http.StatusInternalServerError)
return
}
http.ServeFile(w, r, filePath)
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filename))
+17 -52
View File
@@ -1,39 +1,18 @@
package home
import (
"bytes"
"fmt"
"librapi/services"
"librapi/templates"
"net/http"
"github.com/rs/zerolog/log"
"librapi/forms"
"librapi/services"
"librapi/templates"
)
const URL = "/"
type SearchField struct {
Name string
Value string
Err string
}
type SearchForm struct {
Search SearchField
Error error
Method string
Results []services.BookMetadata
}
func NewSearchForm() SearchForm {
return SearchForm{
Search: SearchField{
Name: "search",
},
Method: http.MethodPost,
}
}
func Handler(bs services.IStore) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
@@ -48,52 +27,38 @@ func Handler(bs services.IStore) func(http.ResponseWriter, *http.Request) {
}
func getHome(w http.ResponseWriter, _ *http.Request) {
home := templates.GetHome()
buf := bytes.NewBufferString("")
if err := home.Execute(buf, &SearchForm{}); err != nil {
log.Err(err).Msg("unable to generate template")
http.Error(w, "unexpected error occurred", http.StatusInternalServerError)
tmpl, err := templates.ExecuteHomeTmpl(&forms.SearchForm{}, w)
if err != nil {
log.Err(err).Msg("unable to generate home template")
return
}
fmt.Fprint(w, buf)
}
func extractSearchForm(r *http.Request) SearchForm {
sf := NewSearchForm()
sf.Search.Value = r.FormValue(sf.Search.Name)
return sf
fmt.Fprint(w, tmpl)
}
func postHome(w http.ResponseWriter, r *http.Request, bs services.IStore) {
home := templates.GetHome()
buf := bytes.NewBufferString("")
sf := extractSearchForm(r)
sf := forms.SearchFormFromRequest(r)
bms, err := bs.Search(sf.Search.Value)
if err != nil {
sf.Error = err
if err := home.Execute(buf, sf); err != nil {
log.Err(err).Msg("unable to generate template")
http.Error(w, "unexpected error occurred", http.StatusInternalServerError)
tmpl, err := templates.ExecuteHomeTmpl(&sf, w)
if err != nil {
log.Err(err).Msg("unable to generate home template")
return
}
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprint(w, buf.String())
fmt.Fprint(w, tmpl)
return
}
sf.Results = bms
if err := home.Execute(buf, sf); err != nil {
log.Err(err).Msg("unable to generate template")
http.Error(w, "unexpected error occurred", http.StatusInternalServerError)
tmpl, err := templates.ExecuteHomeTmpl(&sf, w)
if err != nil {
log.Err(err).Msg("unable to generate home template")
return
}
fmt.Fprint(w, buf)
fmt.Fprint(w, tmpl)
}
+28 -97
View File
@@ -1,58 +1,19 @@
package login
import (
"bytes"
"errors"
"fmt"
"net/http"
"github.com/rs/zerolog/log"
"librapi/forms"
"librapi/services"
"librapi/templates"
)
const URL = "/login"
var (
ErrInvalidUsername = errors.New("username must not be empty")
ErrInvalidPassword = errors.New("password must not be empty")
ErrInvalidCredentials = errors.New("bad credentials")
)
type LoginField struct {
Name string
Value string
Err string
}
type LoginForm struct {
Username LoginField
Password LoginField
Error error
Method string
}
func NewLoginForm() LoginForm {
return LoginForm{
Username: LoginField{
Name: "username",
},
Password: LoginField{
Name: "password",
},
Method: http.MethodPost,
}
}
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()
}
func Handler(a services.IAuthenticate) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
@@ -66,49 +27,27 @@ func Handler(a services.IAuthenticate) func(http.ResponseWriter, *http.Request)
}
}
func extractLoginForm(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 postLogin(w http.ResponseWriter, r *http.Request, a services.IAuthenticate) {
loginForm := templates.GetLoginForm()
loginSuccess := templates.GetLoginSuccess()
if a.IsLogged(r) {
buf := bytes.NewBufferString("")
if err := loginSuccess.Execute(buf, nil); err != nil {
log.Err(err).Msg("unable to generate template")
http.Error(w, "unexpected error occurred", http.StatusInternalServerError)
tmpl, err := templates.ExecuteLoginSuccessTmpl(w)
if err != nil {
log.Err(err).Msg("unable to generate login success template")
return
}
fmt.Fprint(w, buf)
fmt.Fprint(w, tmpl)
return
}
lf := extractLoginForm(r)
lf := forms.LoginFormFromRequest(r)
if lf.HasErrors() {
buf := bytes.NewBufferString("")
if err := loginForm.Execute(buf, &lf); err != nil {
log.Err(err).Msg("unable to generate template")
http.Error(w, "unexpected error occurred", http.StatusInternalServerError)
tmpl, err := templates.ExecuteLoginFormTmpl(&lf, w)
if err != nil {
log.Err(err).Msg("unable to generate login form template")
return
}
w.WriteHeader(http.StatusBadRequest)
fmt.Fprint(w, buf.String())
fmt.Fprint(w, tmpl)
return
}
@@ -117,17 +56,16 @@ func postLogin(w http.ResponseWriter, r *http.Request, a services.IAuthenticate)
if errors.Is(err, services.ErrUnauthorized) {
log.Warn().Str("username", lf.Username.Value).Msg("bad credentials")
lf.Error = ErrInvalidCredentials
lf.Error = forms.ErrInvalidCredentials
buf := bytes.NewBufferString("")
if err := loginForm.Execute(buf, &lf); err != nil {
log.Err(err).Msg("unable to generate template")
http.Error(w, "unexpected error occurred", http.StatusInternalServerError)
tmpl, err := templates.ExecuteLoginFormTmpl(&lf, w)
if err != nil {
log.Err(err).Msg("unable to generate login form template")
return
}
w.WriteHeader(http.StatusUnauthorized)
fmt.Fprint(w, buf.String())
fmt.Fprint(w, tmpl)
return
}
@@ -138,39 +76,32 @@ func postLogin(w http.ResponseWriter, r *http.Request, a services.IAuthenticate)
cookie := session.GenerateCookie(a.IsSecure())
http.SetCookie(w, cookie)
buf := bytes.NewBufferString("")
if err := loginSuccess.Execute(buf, nil); err != nil {
log.Err(err).Msg("unable to generate template")
http.Error(w, "unexpected error occurred", http.StatusInternalServerError)
tmpl, err := templates.ExecuteLoginSuccessTmpl(w)
if err != nil {
log.Err(err).Msg("unable to generate login success template")
return
}
fmt.Fprint(w, buf)
fmt.Fprint(w, tmpl)
}
func getLogin(w http.ResponseWriter, r *http.Request, a services.IAuthenticate) {
loginForm := templates.GetLoginForm()
if a.IsLogged(r) {
loginSuccess := templates.GetLoginSuccess()
buf := bytes.NewBufferString("")
if err := loginSuccess.Execute(buf, nil); err != nil {
log.Err(err).Msg("unable to generate template")
http.Error(w, "unexpected error occurred", http.StatusInternalServerError)
tmpl, err := templates.ExecuteLoginSuccessTmpl(w)
if err != nil {
log.Err(err).Msg("unable to generate login success template")
return
}
fmt.Fprint(w, buf)
fmt.Fprint(w, tmpl)
return
}
buf := bytes.NewBufferString("")
if err := loginForm.Execute(buf, &LoginForm{}); err != nil {
log.Err(err).Msg("unable to generate template")
http.Error(w, "unexpected error occurred", http.StatusInternalServerError)
tmpl, err := templates.ExecuteLoginFormTmpl(&forms.LoginForm{}, w)
if err != nil {
log.Err(err).Msg("unable to generate login form template")
return
}
fmt.Fprint(w, buf)
fmt.Fprint(w, tmpl)
}
+26 -196
View File
@@ -1,129 +1,20 @@
package upload
import (
"bytes"
"errors"
"fmt"
"mime/multipart"
"net/http"
"strconv"
"strings"
"github.com/rs/zerolog/log"
"librapi/forms"
"librapi/services"
"librapi/templates"
)
const (
URL = "/upload"
MaxFileSize = 200 // in MB
URL = "/upload"
)
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]
Description 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",
},
Description: BookField[string]{
Name: "description",
},
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 (bf *BookForm) IntoMetadata() *services.BookMetadata {
bm := &services.BookMetadata{
Name: bf.Name.Value,
Editor: bf.Editor.Value,
Authors: bf.Authors.Value,
Year: uint16(bf.Year.Value),
Keywords: nil,
}
if desc := bf.Description.Value; desc != "" {
bm.Description = &desc
}
if keywords := bf.Keywords.Value; len(keywords) > 0 {
bm.Keywords = keywords
}
return bm
}
func Handler(a services.IAuthenticate, s services.IStore) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
@@ -137,123 +28,62 @@ func Handler(a services.IAuthenticate, s services.IStore) func(http.ResponseWrit
}
}
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
bf.Description.Value = r.FormValue(bf.Description.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, s services.IStore) {
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)
tmpl, err := templates.ExecuteUploadFormTmpl(&forms.UploadForm{Error: services.ErrUnauthorized.Error()}, w)
if err != nil {
log.Err(err).Msg("unable to generate upload template")
return
}
w.WriteHeader(http.StatusUnauthorized)
fmt.Fprint(w, buf.String())
fmt.Fprint(w, tmpl)
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)
uf := forms.UploadFormFromRequest(r)
tmpl, err := templates.ExecuteUploadFormTmpl(&uf, w)
if err != nil {
log.Err(err).Msg("unable to generate upload template")
return
}
if bf.HasErrors() {
if uf.HasErrors() {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprint(w, buf.String())
fmt.Fprint(w, tmpl)
return
}
filename := bf.File.Value.GetFilename()
filename := uf.File.Value.GetFilename()
log.Info().Str("filename", filename).Msg("file is uploading...")
if err := s.Save(bf.IntoMetadata(), bf.File.Value.file); err != nil {
if err := uploadForm.Execute(buf, &BookForm{Error: err.Error()}); err != nil {
log.Err(err).Msg("unable to generate template")
http.Error(w, "unexpected error occurred", http.StatusInternalServerError)
if err := s.Save(uf.IntoMetadata(), uf.File.Value.File); err != nil {
tmpl, err := templates.ExecuteUploadFormTmpl(&uf, w)
if err != nil {
log.Err(err).Msg("unable to generate upload template")
return
}
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprint(w, buf.String())
fmt.Fprint(w, tmpl)
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)
tmpl, err = templates.ExecuteUploadFormTmpl(&forms.UploadForm{Method: http.MethodPost}, w)
if err != nil {
log.Err(err).Msg("unable to generate upload template")
return
}
fmt.Fprint(w, buf.String())
fmt.Fprint(w, tmpl)
}
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)
tmpl, err := templates.ExecuteUploadFormTmpl(&forms.UploadForm{}, w)
if err != nil {
log.Err(err).Msg("unable to generate upload template")
return
}
fmt.Fprint(w, buf)
fmt.Fprint(w, tmpl)
}