From 9c91061cf195f490d78fa1201c6a442a310a6d26 Mon Sep 17 00:00:00 2001 From: Knut Ahlers Date: Wed, 6 Sep 2023 19:54:02 +0200 Subject: [PATCH] Allow to request log instead of PDF on error Signed-off-by: Knut Ahlers --- README.md | 4 +++- assets.go | 9 +++++---- main.go | 9 ++++++++- processing.go | 19 +++++++++++++++---- 4 files changed, 31 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 007b5db..d2e43f1 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ main.log main.pdf # Using the default-env and exchanging a TeX file for a PDF -# curl -sSL -H 'Accept: application/pdf' --data-binary @main.tex -o main.pdf localhost:3000/job +# curl -L -H 'Accept: application/pdf' --data-binary @main.tex -OJ localhost:3000/job ``` What happened here is we packed all assets required for generating the letter into the ZIP archive, pushed it to the API, waited for it to build a TAR and extracted the resulting files from it. @@ -69,3 +69,5 @@ GET /job/{uuid}/download Download the resulting archive (You may specify an Accept header to select whether to receive a ZIP, a TAR archive or just the raw PDF.) ``` + +All routes accept the `log-on-error` parameter: If set a PDF download (`Accept` header set to `application/pdf`) will return the log instead of the PDF if no PDF is found. diff --git a/assets.go b/assets.go index 24af4da..ae010ec 100644 --- a/assets.go +++ b/assets.go @@ -5,6 +5,7 @@ import ( "archive/zip" "bytes" "io" + "io/fs" "os" "path" "path/filepath" @@ -109,7 +110,7 @@ func buildAssetsZIP(uid uuid.UUID) (io.Reader, error) { return buf, errors.Wrap(w.Close(), "closing zip file") } -func getAssetsPDF(uid uuid.UUID) (io.Reader, error) { +func getAssetsFile(uid uuid.UUID, ext string) (io.Reader, error) { var ( buf = new(bytes.Buffer) found bool @@ -121,7 +122,7 @@ func getAssetsPDF(uid uuid.UUID) (io.Reader, error) { return err } - if path.Ext(info.Name()) != ".pdf" { + if path.Ext(info.Name()) != ext { return nil } @@ -131,7 +132,7 @@ func getAssetsPDF(uid uuid.UUID) (io.Reader, error) { } defer func() { if err := osFile.Close(); err != nil { - logrus.WithError(err).Error("closing output pdf file (leaked fd)") + logrus.WithError(err).Error("closing output file (leaked fd)") } }() @@ -145,7 +146,7 @@ func getAssetsPDF(uid uuid.UUID) (io.Reader, error) { if !found { // We found no file - return nil, errors.New("no pdf found") + return nil, fs.ErrNotExist } return buf, errors.Wrap(err, "walking source dir") diff --git a/main.go b/main.go index 5e5d92b..97e7052 100644 --- a/main.go +++ b/main.go @@ -3,6 +3,7 @@ package main import ( "fmt" "io" + "io/fs" "net/http" "os" "time" @@ -109,8 +110,14 @@ func downloadAssets(res http.ResponseWriter, r *http.Request) { case "application/pdf": contentType = "application/pdf" - content, err = getAssetsPDF(uid) filename = uid.String() + ".pdf" + content, err = getAssetsFile(uid, ".pdf") + + if errors.Is(err, fs.ErrNotExist) && r.URL.Query().Has("log-on-error") { + contentType = "application/octet-stream" + filename = uid.String() + ".log" + content, err = getAssetsFile(uid, ".log") + } default: content, err = buildAssetsZIP(uid) diff --git a/processing.go b/processing.go index a8cb025..4880d35 100644 --- a/processing.go +++ b/processing.go @@ -6,7 +6,6 @@ import ( "io" "math" "net/http" - "net/url" "os" "os/exec" "path" @@ -133,6 +132,7 @@ func startNewJob(res http.ResponseWriter, r *http.Request) { go jobProcessor(jobUUID) u := urlMust(router.Get("waitForJob").URL("uid", jobUUID.String())) + u.RawQuery = r.URL.Query().Encode() http.Redirect(res, r, u.String(), http.StatusFound) } @@ -163,19 +163,30 @@ func waitForJob(res http.ResponseWriter, r *http.Request) { fallthrough case statusStarted: - u := urlMust(router.Get("waitForJob").URL("uid", uid.String())) - u.RawQuery = url.Values{"loop": []string{strconv.Itoa(loop)}}.Encode() - <-time.After(time.Duration(math.Pow(sleepBase, float64(loop))) * time.Second) + params := r.URL.Query() + params.Set("loop", strconv.Itoa(loop)) + + u := urlMust(router.Get("waitForJob").URL("uid", uid.String())) + u.RawQuery = params.Encode() + http.Redirect(res, r, u.String(), http.StatusFound) return case statusError: + if r.URL.Query().Has("log-on-error") { + u := urlMust(router.Get("downloadAssets").URL("uid", uid.String())) + u.RawQuery = r.URL.Query().Encode() + http.Redirect(res, r, u.String(), http.StatusFound) + return + } + http.Error(res, "Processing ran into an error.", http.StatusInternalServerError) case statusFinished: u := urlMust(router.Get("downloadAssets").URL("uid", uid.String())) + u.RawQuery = r.URL.Query().Encode() http.Redirect(res, r, u.String(), http.StatusFound) } }