From b70ee7fd07c1bb5fc42441b555da6b4ef99a6b4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Omar=20Vergara=20P=C3=A9rez?= Date: Tue, 10 Sep 2024 23:31:31 -0600 Subject: [PATCH 01/11] chore(main): Add graceful shutdown to the http service --- main.go | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 87 insertions(+), 10 deletions(-) diff --git a/main.go b/main.go index ca7a23b..cbbcc9b 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,7 @@ package main import ( "bytes" + "context" "embed" "errors" "fmt" @@ -10,8 +11,12 @@ import ( "log" "net/http" "os" + "os/signal" "path/filepath" "strings" + "sync" + "syscall" + "time" "github.com/gabriel-vasile/mimetype" "github.com/go-chi/chi/v5" @@ -290,11 +295,16 @@ func handleFileFormat(w http.ResponseWriter, r *http.Request) error { } tmpl, err := template.ParseFS(templatesHTML, templates...) - if err = tmpl.ExecuteTemplate(w, "format-elements", f.SupportedFormats()); err != nil { + if err != nil { log.Printf("error occurred parsing template files: %v", err) return WithHTTPStatus(err, http.StatusInternalServerError) } + if err = tmpl.ExecuteTemplate(w, "format-elements", f.SupportedFormats()); err != nil { + log.Printf("error occurred executing template files: %v", err) + return WithHTTPStatus(err, http.StatusInternalServerError) + } + return nil } @@ -320,13 +330,7 @@ func handleModal(w http.ResponseWriter, r *http.Request) error { return nil } -func main() { - port := os.Getenv("MORPHOS_PORT") - // default port. - if port == "" { - port = "8080" - } - +func newRouter() http.Handler { r := chi.NewRouter() r.Use(middleware.Logger) @@ -335,21 +339,90 @@ func main() { var staticFS = http.FS(staticFiles) fs := http.FileServer(staticFS) + addRoutes(r, fs, fsUpload) + + return r +} + +func addRoutes(r *chi.Mux, fs, fsUpload http.Handler) { + r.HandleFunc("/healthz", healthz) r.Handle("/static/*", fs) r.Handle("/files/*", http.StripPrefix("/files", fsUpload)) r.Get("/", toHandler(index)) r.Post("/upload", toHandler(handleUploadFile)) r.Post("/format", toHandler(handleFileFormat)) r.Get("/modal", toHandler(handleModal)) +} + +func run(ctx context.Context) error { + port := os.Getenv("MORPHOS_PORT") + + // default port. + if port == "" { + port = "8080" + } - http.ListenAndServe(fmt.Sprintf(":%s", port), r) + ctx, stop := signal.NotifyContext(ctx, + os.Interrupt, + syscall.SIGTERM, + syscall.SIGQUIT) + defer stop() + + r := newRouter() + + srv := &http.Server{ + Addr: fmt.Sprintf(":%s", port), + Handler: r, + } + + go func() { + if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { + fmt.Fprintf(os.Stderr, "error listening and serving: %s\n", err) + } + }() + + var wg sync.WaitGroup + wg.Add(1) + + go func() { + defer wg.Done() + <-ctx.Done() + + log.Println("shutdown signal received") + + ctxTimeout, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + srv.SetKeepAlivesEnabled(false) + + if err := srv.Shutdown(ctxTimeout); err != nil { + fmt.Fprintf(os.Stderr, "error shutting down http server: %s\n", err) + } + + log.Println("shutdown completed") + }() + + wg.Wait() + + return nil +} + +func main() { + ctx := context.Background() + + if err := run(ctx); err != nil { + fmt.Fprintf(os.Stderr, "%s\n", err) + os.Exit(1) + } + + log.Println("exiting...") } // renderError functions executes the error template. func renderError(w http.ResponseWriter, message string, statusCode int) { w.WriteHeader(statusCode) tmpl, _ := template.ParseFS(templatesHTML, "templates/partials/error.tmpl") - tmpl.ExecuteTemplate(w, "error", struct{ ErrorMessage string }{ErrorMessage: message}) + _ = tmpl.ExecuteTemplate(w, "error", struct{ ErrorMessage string }{ErrorMessage: message}) } func fileNameWithoutExtension(fileName string) string { @@ -359,3 +432,7 @@ func fileNameWithoutExtension(fileName string) string { func filename(filename, extension string) string { return fmt.Sprintf("%s.%s", fileNameWithoutExtension(filename), extension) } + +func healthz(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) +} From 8ed4535a280f7afffe24be68e0232e6ffff78b39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Omar=20Vergara=20P=C3=A9rez?= Date: Thu, 12 Sep 2024 23:10:27 -0600 Subject: [PATCH 02/11] build(compose): add a health check to the compose file --- docker-compose.yml | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 97a3338..822299a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,8 +1,14 @@ name: morphos services: - morphos-server: - ports: - - 8080:8080 - volumes: - - /tmp:/tmp - image: ghcr.io/danvergara/morphos-server:latest + morphos-server: + image: ghcr.io/danvergara/morphos-server:latest + ports: + - 8080:8080 + volumes: + - /tmp:/tmp + healthcheck: + test: timeout 10s bash -c ':> /dev/tcp/127.0.0.1/8080' || exit 1 + interval: 60s + retries: 3 + start_period: 20s + timeout: 30s From c8d2a1922d64281a57ffe9c9ab35b983e88e9ce4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Omar=20Vergara=20P=C3=A9rez?= Date: Thu, 12 Sep 2024 23:12:32 -0600 Subject: [PATCH 03/11] build(main): rename the temporary path environment variable --- README.md | 7 +++++++ main.go | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ed005ec..de3c26f 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,13 @@ A modal will pop up with a preview of the converted image. +### Configuration + +The configuration is only done by the environment varibles shown below. + +* `MORPHOS_PORT` changes the port the server will listen to (default is `8080`) +* `MORPHOS_UPLOAD_PATH` defines the temporary path the files will be stored on disk (default is `/tmp`) + ## Supported Files And Convert Matrix ### Images X Images diff --git a/main.go b/main.go index cbbcc9b..d4a9be0 100644 --- a/main.go +++ b/main.go @@ -44,7 +44,7 @@ var ( ) func init() { - uploadPath = os.Getenv("TMP_DIR") + uploadPath = os.Getenv("MORPHOS_UPLOAD_PATH") if uploadPath == "" { uploadPath = "/tmp" } From 26b0a180376f4165883f6e1e8179eaa028e8c5a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Omar=20Vergara=20P=C3=A9rez?= Date: Thu, 12 Sep 2024 23:13:26 -0600 Subject: [PATCH 04/11] build(make): add changes to the makefile --- Makefile | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index da73c54..a937470 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,6 @@ HTMX_VERSION=1.9.6 RESPONSE_TARGETS_VERSION=1.9.11 BOOTSTRAP_VERSION=5.3.2 -GO_VERSION=1.21.5 .PHONY: run ## run: Runs the air command. @@ -27,11 +26,11 @@ download-bootstrap: .PHONY: docker-build ## docker-build: Builds the container image docker-build: - docker build --build-arg="GO_VERSION=${GO_VERSION}" -t morphos . + docker build -t morphos . .PHONY: docker-run ## docker-run: Runs the container -docker-run: +docker-run: docker run --rm -p 8080:8080 -v /tmp:/tmp morphos .PHONY: help From cc4217966028b3dec96834dc061002a20f21a9f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Omar=20Vergara=20P=C3=A9rez?= Date: Thu, 12 Sep 2024 23:53:13 -0600 Subject: [PATCH 05/11] fix(templates): fix zip files preview --- static/zip-icon.png | Bin 0 -> 5142 bytes templates/partials/active_modal.tmpl | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 static/zip-icon.png diff --git a/static/zip-icon.png b/static/zip-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..c137c25b0e945dd0c4dceefcdbeb4f5016f33b17 GIT binary patch literal 5142 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 {{else if eq .FileType "application"}} - + Responsive image {{end}}