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.
This commit is contained in:
tamsin woo 2024-02-24 21:07:34 -08:00
commit 7e0c3aacae
11 changed files with 145 additions and 0 deletions

0
.gitignore vendored Normal file
View File

15
README.md Normal file
View File

@ -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

15
cmd/web/handlers.go Normal file
View File

@ -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"))
}

26
cmd/web/logger.go Normal file
View File

@ -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
}

44
cmd/web/main.go Normal file
View File

@ -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)
}

25
cmd/web/routes.go Normal file
View File

@ -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)
}

8
go.mod Normal file
View File

@ -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
)

4
go.sum Normal file
View File

@ -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=

8
ui/efs.go Normal file
View File

@ -0,0 +1,8 @@
package ui
import (
"embed"
)
//go:embed "html" "static"
var Files embed.FS

0
ui/html/base.tmpl Normal file
View File

0
ui/static/index.html Normal file
View File