diff --git a/handlers/home/handler.go b/handlers/home/handler.go index 4a2d782..0dcab50 100644 --- a/handlers/home/handler.go +++ b/handlers/home/handler.go @@ -9,6 +9,8 @@ import ( "github.com/rs/zerolog/log" ) +const URL = "/" + func Handler() func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { switch r.Method { diff --git a/handlers/login/handler.go b/handlers/login/handler.go index d7e964f..946a8e9 100644 --- a/handlers/login/handler.go +++ b/handlers/login/handler.go @@ -12,6 +12,8 @@ import ( "librapi/templates" ) +const URL = "/login" + var ( ErrInvalidUsername = errors.New("username must not be empty") ErrInvalidPassword = errors.New("password must not be empty") diff --git a/handlers/upload/handler.go b/handlers/upload/handler.go index c2ad48a..2b8e89a 100644 --- a/handlers/upload/handler.go +++ b/handlers/upload/handler.go @@ -4,10 +4,8 @@ import ( "bytes" "errors" "fmt" - "io" "mime/multipart" "net/http" - "os" "strconv" "strings" @@ -17,7 +15,10 @@ import ( "librapi/templates" ) -const MaxFileSize = 200 // in MB +const ( + URL = "/upload" + MaxFileSize = 200 // in MB +) var ( ErrInvalidName = errors.New("book name must not be empty") @@ -99,13 +100,13 @@ func (bf *BookForm) IsSuccess() bool { return bf.Method == http.MethodPost && bf.Error == "" && !bf.HasErrors() } -func Handler(a services.IAuthenticate) func(http.ResponseWriter, *http.Request) { +func Handler(a services.IAuthenticate, s services.IStore) 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) + postUploadFile(w, r, a, s) default: http.Error(w, "method not allowed", http.StatusMethodNotAllowed) } @@ -163,7 +164,7 @@ func extractBookForm(r *http.Request) BookForm { return bf } -func postUploadFile(w http.ResponseWriter, r *http.Request, a services.IAuthenticate) { +func postUploadFile(w http.ResponseWriter, r *http.Request, a services.IAuthenticate, s services.IStore) { uploadForm := templates.GetUploadForm() if !a.IsLogged(r) { @@ -171,6 +172,7 @@ func postUploadFile(w http.ResponseWriter, r *http.Request, a services.IAuthenti 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) + return } w.WriteHeader(http.StatusUnauthorized) @@ -183,6 +185,7 @@ func postUploadFile(w http.ResponseWriter, r *http.Request, a services.IAuthenti if err := uploadForm.Execute(buf, &bf); err != nil { log.Err(err).Msg("unable to generate template") http.Error(w, "unexpected error occurred", http.StatusInternalServerError) + return } if bf.HasErrors() { @@ -194,9 +197,8 @@ func postUploadFile(w http.ResponseWriter, r *http.Request, a services.IAuthenti 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 { + if err := s.Save(filename, 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) } @@ -206,20 +208,6 @@ func postUploadFile(w http.ResponseWriter, r *http.Request, a services.IAuthenti 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") diff --git a/main.go b/main.go index a5970b3..a9387aa 100644 --- a/main.go +++ b/main.go @@ -27,16 +27,18 @@ func main() { defer fnCancel() auth := services.NewAuthentication(ctx) + bs := services.NewBookStore(services.GetEnv().GetDir()) srv := server.NewServer( ctx, services.GetEnv().GetPort(), - server.NewHandler("/", home.Handler()), - server.NewHandler("/upload", upload.Handler(auth)), - server.NewHandler("/login", login.Handler(auth)), + server.NewHandler(home.URL, home.Handler()), + server.NewHandler(upload.URL, upload.Handler(auth, bs)), + server.NewHandler(login.URL, login.Handler(auth)), ) srv.Serve() <-srv.Done() <-auth.Done() + <-bs.Done() } diff --git a/services/book_store.go b/services/book_store.go new file mode 100644 index 0000000..8b97d1b --- /dev/null +++ b/services/book_store.go @@ -0,0 +1,69 @@ +package services + +import ( + "errors" + "io" + "os" + "path/filepath" + "sync" + + "github.com/rs/zerolog/log" +) + +var ( + ErrFileCreation = errors.New("unexpected error occurred while creating book file") + ErrFileCopy = errors.New("unexpected error occurred while copying book file") +) + +type IStore interface { + Save(name string, content io.ReadCloser) error +} + +var _ IStore = (*BookStore)(nil) + +type BookStore struct { + processing *sync.WaitGroup + dir string +} + +func NewBookStore(dir string) *BookStore { + if err := os.MkdirAll(dir, 0755); err != nil { //nolint + log.Fatal().Err(err).Msg("unable to create store dir") + } + + return &BookStore{ + processing: &sync.WaitGroup{}, + dir: dir, + } +} + +func (bs *BookStore) Done() <-chan struct{} { + chDone := make(chan struct{}) + go func() { + bs.processing.Wait() + chDone <- struct{}{} + log.Info().Msg("book store processing done") + }() + return chDone +} + +func (bs *BookStore) Save(name string, content io.ReadCloser) error { + bs.processing.Add(1) + defer bs.processing.Done() + + defer content.Close() + + dst, err := os.Create(filepath.Join(GetEnv().GetDir(), name)) + if err != nil { + log.Err(err).Msg(ErrFileCreation.Error()) + return ErrFileCreation + } + defer dst.Close() + + if _, err := io.Copy(dst, content); err != nil { + log.Err(err).Msg(ErrFileCopy.Error()) + return ErrFileCopy + } + + return nil +} diff --git a/services/environments.go b/services/environments.go index 20a5b8b..6f3223b 100644 --- a/services/environments.go +++ b/services/environments.go @@ -71,11 +71,6 @@ func newEnv() environment { if storeDir == "" { storeDir = defaultMainDir } - - if err := os.MkdirAll(storeDir, 0755); err != nil { //nolint - log.Fatal().Err(err).Msg("unable to create store dir") - } - env.storeDir = storeDir return env