commit 7e0c3aacaef726f680e8d9bec27c045f3bed0a49 Author: tamsin woo Date: Sat Feb 24 21:07:34 2024 -0800 bootstrap a go application planning to make this some kind of unhinged microfrontend hotwired mess. this is basically the setup walked through in "Lets Go", but pared down. since we plan to ship this in k8s behind nginx and cert-manager, there's no need to do TLS termination in go. i like httprouter and alice, so i'm including them here early. use `go:embed` to get our templates and static assets (neither of which exist yet) into the binary so we can ship this whole thing to a commodity `go` container. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/README.md b/README.md new file mode 100644 index 0000000..92d354c --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +replication.systems +=================== + +it's a [`go`][golang] + [`hotwire`][hotwire] mess! + +[wtf.replication.tech][wtf] +--------------------------- + +TK: planning to put some stuff about how this made and deployed at the above +address. + + +[golang]: https://go.dev/ +[hotwire]: https://hotwired.dev/ +[wtf]: wtf.replication.tech diff --git a/cmd/web/handlers.go b/cmd/web/handlers.go new file mode 100644 index 0000000..476bc7d --- /dev/null +++ b/cmd/web/handlers.go @@ -0,0 +1,15 @@ +package main + +import ( + "net/http" +) + +// home ... +func (app *application) home(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/" { + http.NotFound(w, r) + return + } + + w.Write([]byte("Hello from replication.systems")) +} diff --git a/cmd/web/logger.go b/cmd/web/logger.go new file mode 100644 index 0000000..f466c00 --- /dev/null +++ b/cmd/web/logger.go @@ -0,0 +1,26 @@ +package main + +import ( + "log/slog" + "os" +) + +// loggerBuilder ... +func loggerBuilder(logfmt *string, loglevel *string) *slog.Logger { + var logger *slog.Logger + var handler_options slog.HandlerOptions + + switch *loglevel { + case "DEBUG": handler_options = slog.HandlerOptions{Level: slog.LevelDebug} + case "INFO": handler_options = slog.HandlerOptions{Level: slog.LevelInfo} + case "WARN": handler_options = slog.HandlerOptions{Level: slog.LevelWarn} + case "ERROR": handler_options = slog.HandlerOptions{Level: slog.LevelError} + } + + switch *logfmt { + case "json": logger = slog.New(slog.NewJSONHandler(os.Stdout, &handler_options)) + default: logger = slog.New(slog.NewTextHandler(os.Stdout, &handler_options)) + } + + return logger +} diff --git a/cmd/web/main.go b/cmd/web/main.go new file mode 100644 index 0000000..5bfe849 --- /dev/null +++ b/cmd/web/main.go @@ -0,0 +1,44 @@ +package main + +import ( + "flag" + "log/slog" + "net/http" + "os" + "time" +) + +type application struct { + logger *slog.Logger +} + +// main run the webserverrrrr +func main() { + // configuration + addr := flag.String("addr", ":4000", "HTTP network address") + logfmt := flag.String("logfmt", "text", "Log output format") + loglevel := flag.String("loglevel", "INFO", "Log level: DEBUG, INFO, WARN, or ERROR") + flag.Parse() + + // setup the application + app := &application{ + logger: loggerBuilder(logfmt, loglevel), + } + + + srv := &http.Server{ + Addr: *addr, + Handler: app.routes(), + ErrorLog: slog.NewLogLogger(app.logger.Handler(), slog.LevelError), + IdleTimeout: time.Minute, + ReadTimeout: 5 * time.Second, + WriteTimeout: 10 * time.Second, + } + + app.logger.Info("starting server", slog.String("addr", *addr)) + + err := srv.ListenAndServe() + + app.logger.Error(err.Error()) + os.Exit(1) +} diff --git a/cmd/web/routes.go b/cmd/web/routes.go new file mode 100644 index 0000000..ed8b9ce --- /dev/null +++ b/cmd/web/routes.go @@ -0,0 +1,25 @@ +package main + +import ( + "net/http" + + "github.com/julienschmidt/httprouter" + "github.com/justinas/alice" + + "replication-systems.estradiol.cloud/ui" +) + +// routes ... +func (app *application) routes() http.Handler { + router := httprouter.New() + + // setup server for static files + fileServer := http.FileServer(http.FS(ui.Files)) + router.Handler(http.MethodGet, "/static/*filepath", fileServer) + + router.HandlerFunc(http.MethodGet, "/", app.home) + + standard := alice.New() + + return standard.Then(router) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..e2be010 --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module replication-systems.estradiol.cloud + +go 1.21.4 + +require ( + github.com/julienschmidt/httprouter v1.3.0 + github.com/justinas/alice v1.2.0 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..bdc18c1 --- /dev/null +++ b/go.sum @@ -0,0 +1,4 @@ +github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/justinas/alice v1.2.0 h1:+MHSA/vccVCF4Uq37S42jwlkvI2Xzl7zTPCN5BnZNVo= +github.com/justinas/alice v1.2.0/go.mod h1:fN5HRH/reO/zrUflLfTN43t3vXvKzvZIENsNEe7i7qA= diff --git a/ui/efs.go b/ui/efs.go new file mode 100644 index 0000000..9acafcd --- /dev/null +++ b/ui/efs.go @@ -0,0 +1,8 @@ +package ui + +import ( + "embed" +) + +//go:embed "html" "static" +var Files embed.FS diff --git a/ui/html/base.tmpl b/ui/html/base.tmpl new file mode 100644 index 0000000..e69de29 diff --git a/ui/static/index.html b/ui/static/index.html new file mode 100644 index 0000000..e69de29