From 21001c80ac418b2b389ddba3f886ffd96ecaa975 Mon Sep 17 00:00:00 2001 From: Julien Dessaux Date: Tue, 11 Mar 2025 23:53:08 +0100 Subject: [PATCH 1/2] chore(tooling): remove unused tooling --- .gitignore | 1 - Dockerfile | 18 ----------- GNUmakefile | 92 +++++++++++++++++++---------------------------------- 3 files changed, 32 insertions(+), 79 deletions(-) delete mode 100644 Dockerfile diff --git a/.gitignore b/.gitignore index 2cfc200..a4730c6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ -checksums ods diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 990daf0..0000000 --- a/Dockerfile +++ /dev/null @@ -1,18 +0,0 @@ -##### Build #################################################################### -FROM docker.io/library/golang:1.23-alpine - -WORKDIR /usr/src/app - -COPY checksums / -RUN CGO_ENABLED=0 go build -o /usr/local/bin/ods ./ - -##### Run ###################################################################### -FROM alpine:3.20 - -ENV LANG en_US.utf8 - -RUN apk upgrade --no-cache - -COPY --from=0 /usr/local/bin/ods /usr/local/bin/ - -ENTRYPOINT ["/usr/local/bin/ods"] diff --git a/GNUmakefile b/GNUmakefile index c86fa6b..483f13b 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -5,12 +5,40 @@ SHELL := bash MAKEFLAGS += --warn-undefined-variables MAKEFLAGS += --no-builtin-rules .DEFAULT_GOAL := help -OUTDIR := "." -CONTAINER_REGISTRY ?= localhost -CONTAINER_TAG ?= latest +##### Quality ################################################################## +.PHONY: check +check: ## run all code checks + go mod verify + go vet ./... -DEPS := Dockerfile GNUmakefile go.mod go.sum index.html main.go ods.txt +.PHONY: tidy +tidy: ## tidy up the code + go fmt ./... + go mod tidy -v + +##### Development ############################################################## +.PHONY: build +build: git-crypt-unlocked ## build the code + CGO_ENABLED=0 go build -o ./ods ./ + +.PHONY: clean +clean: ## clean the code + rm -f ./ods + +.PHONY: run +run: git-crypt-unlocked ## run the code + go run ./ + +##### Operations ############################################################### +.PHONY: push +push: tidy no-dirty check ## push changes to git remote + git push git main + +.PHONY: deploy +deploy: build ## deploy changes to the production server + rsync ./ods root@ods.adyxax.org:/usr/local/bin/ + ssh root@ods.adyxax.org "systemctl restart ods" ##### Utils #################################################################### .PHONY: confirm @@ -34,59 +62,3 @@ help: .PHONY: no-dirty no-dirty: git diff --exit-code - -##### Quality ################################################################## -.PHONY: check -check: ## run all code checks - go mod verify - go vet ./... - -.PHONY: tidy -tidy: ## tidy up the code - go fmt ./... - go mod tidy -v - -##### Development ############################################################## -.PHONY: build -build: ods ## build the code - -.PHONY: clean -clean: ## clean the code - rm -f $(OUTDIR)/ods - -ods: git-crypt-unlocked $(DEPS) - CGO_ENABLED=0 go build -o $(OUTDIR)/ods ./ - -.PHONY: git-crypt-unlocked ## run -run: ## run the code - go run ./ - -##### Containers ############################################################### -.PHONY: container-build -container-build: git-crypt-unlocked ## build the container image - @printf $(DEPS) | xargs shasum >checksums - podman build \ - -v $$PWD:/usr/src/app \ - -t $(CONTAINER_REGISTRY)/ods:$(CONTAINER_TAG) \ - . - -.PHONY: container-push -container-push: ## push the container image to the container registry - podman push \ - $(CONTAINER_REGISTRY)/ods:$(CONTAINER_TAG) - -.PHONY: container-run -container-run: ## run the code inside podman - podman run --rm -ti \ - -p 8090:8090 \ - $(CONTAINER_REGISTRY)/ods:$(CONTAINER_TAG) - -##### Operations ############################################################### -.PHONY: push -push: tidy no-dirty check ## push changes to git remote - git push git master - -.PHONY: deploy -deploy: ods ## deploy changes to the production server - rsync $(OUTDIR)/ods root@ods.adyxax.org:/usr/local/bin/ - ssh root@ods.adyxax.org "systemctl restart ods" From 89118c45d1d63f43edb48e91453fad362283c9eb Mon Sep 17 00:00:00 2001 From: Julien Dessaux Date: Wed, 12 Mar 2025 00:54:55 +0100 Subject: [PATCH 2/2] chore(ods): rewrite the core http server handling --- index.html | 4 +- main.go | 110 +++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 87 insertions(+), 27 deletions(-) diff --git a/index.html b/index.html index f98fd30..c01d42d 100644 --- a/index.html +++ b/index.html @@ -14,13 +14,13 @@
- {{ if .HasQuery }} + {{ if ne .Query "" }} {{ end }}

- {{ if .HasQuery }} + {{ if ne .Query "" }} {{ .Query }} est un mot IN{{ else }}valid">{{ end }}VALIDE au scrabble. {{ end }}

diff --git a/main.go b/main.go index ab42630..7cd4630 100644 --- a/main.go +++ b/main.go @@ -1,20 +1,23 @@ package main import ( + "context" "embed" + "fmt" + "html/template" + "log/slog" + "net" + "net/http" + "os" + "os/signal" + "strings" + "syscall" + "time" + "unicode" + "golang.org/x/text/runes" "golang.org/x/text/transform" "golang.org/x/text/unicode/norm" - "html/template" - "log/slog" - "net/http" - "strings" - "unicode" -) - -// Variables to customise the search -const ( - listenStr = "0.0.0.0:8090" ) //go:embed ods.txt @@ -30,16 +33,14 @@ var static embed.FS var indexTemplate = template.Must(template.New("index").ParseFS(templatesFS, "index.html")) type IndexData struct { - HasQuery bool - Query string - Invalid bool + Invalid bool + Query string } func getIndex() http.Handler { data := IndexData{ - HasQuery: false, - Query: "", - Invalid: true, + Query: "", + Invalid: true, } return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Cache-Control", "no-store, no-cache") @@ -52,9 +53,8 @@ func postIndex() http.Handler { words := strings.Split(ods, "\n") return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { data := IndexData{ - HasQuery: true, - Query: r.FormValue("query"), - Invalid: true, + Query: r.FormValue("query"), + Invalid: true, } query, _, _ := transform.String(normalizer, strings.TrimSpace(strings.ToUpper(data.Query))) for _, w := range words { @@ -68,12 +68,72 @@ func postIndex() http.Handler { }) } +func run( + ctx context.Context, + getenv func(string) string, +) error { + ctx, cancel := signal.NotifyContext(ctx, os.Interrupt, syscall.SIGTERM) + defer cancel() + + mux := http.NewServeMux() + mux.Handle("GET /static/", http.FileServer(http.FS(static))) + mux.Handle("GET /", getIndex()) + mux.Handle("POST /", postIndex()) + + host := getenv("ODS_HOST") + if host == "" { + host = "127.0.0.1" + } + port := getenv("ODS_PORT") + if port == "" { + port = "8080" + } + + httpServer := &http.Server{ + Addr: net.JoinHostPort(host, port), + Handler: mux, + } + errChan := make(chan error, 1) + go func() { + slog.Info("backend http server listening", "address", httpServer.Addr) + if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed { + errChan <- fmt.Errorf("error listening and serving backend http server on %s: %w", httpServer.Addr, err) + } + }() + + select { + case err := <-errChan: + return err + case <-ctx.Done(): + slog.Info("backend http server shutting down") + shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 10*time.Second) + defer shutdownCancel() + if err := httpServer.Shutdown(shutdownCtx); err != nil { + return fmt.Errorf("error shutting down backend http server: %w", err) + } + } + + return nil +} + func main() { - http.Handle("GET /static/", http.FileServer(http.FS(static))) - http.Handle("GET /", getIndex()) - http.Handle("POST /", postIndex()) - slog.Info("listening", "addr", listenStr) - if err := http.ListenAndServe(listenStr, nil); err != nil && err != http.ErrServerClosed { - slog.Error("error listening and serving", "error", err) + ctx := context.Background() + + var opts *slog.HandlerOptions + if os.Getenv("ODS_DEBUG") != "" { + opts = &slog.HandlerOptions{ + AddSource: true, + Level: slog.LevelDebug, + } + } + logger := slog.New(slog.NewJSONHandler(os.Stdout, opts)) + slog.SetDefault(logger) + + if err := run( + ctx, + os.Getenv, + ); err != nil { + fmt.Fprintf(os.Stderr, "%+v\n", err) + os.Exit(1) } }