mood/inspo boards

middleware refactors

besaid.zone 92eb6e01 477d6a23

verified
Changed files
+52 -34
cmd
internal
+17 -12
cmd/main.go
··· 3 3 import ( 4 4 "context" 5 5 "os" 6 + "time" 6 7 7 8 "github.com/charmbracelet/log" 8 9 _ "github.com/joho/godotenv/autoload" ··· 10 11 "tangled.org/dane.is.extraordinarily.cool/pallet/internal/db" 11 12 "tangled.org/dane.is.extraordinarily.cool/pallet/internal/server" 12 13 ) 13 - 14 14 15 15 func main() { 16 16 if err := run(os.Args); err != nil { ··· 21 21 22 22 func run(args []string) error { 23 23 app := &cli.Command{ 24 - Name: "pallet", 24 + Name: "pallet", 25 25 Usage: "pallet app server", 26 26 } 27 27 28 28 app.Flags = []cli.Flag{ 29 29 &cli.StringFlag{ 30 - Name: "log-level", 31 - Usage: "log verbosity level (eg: warn, info, debug)", 30 + Name: "log-level", 31 + Usage: "log verbosity level (eg: warn, info, debug)", 32 32 Sources: cli.EnvVars("GO_LOG_LEVEL", "LOG_LEVEL"), 33 33 }, 34 34 } 35 35 36 36 app.Commands = []*cli.Command{ 37 37 &cli.Command{ 38 - Name: "start", 39 - Usage: "start the pallet app server", 38 + Name: "start", 39 + Usage: "start the pallet app server", 40 40 Action: runServer, 41 41 Flags: []cli.Flag{ 42 42 &cli.StringFlag{ 43 - Name: "port", 44 - Usage: "Port to bind the HTTP server to", 45 - Value: ":8080", 43 + Name: "port", 44 + Usage: "Port to bind the HTTP server to", 45 + Value: ":8080", 46 46 Sources: cli.EnvVars("PORT"), 47 47 }, 48 48 }, 49 49 }, 50 50 } 51 - 52 51 53 52 return app.Run(context.Background(), args) 54 53 } 55 54 56 55 func runServer(ctx context.Context, cmd *cli.Command) error { 56 + appContext, cancel := context.WithTimeout(ctx, time.Minute*5) 57 + defer cancel() 57 58 logger := log.New(os.Stdout) 58 59 db, err := db.NewSQLiteDB() 59 60 if err != nil { ··· 61 62 } 62 63 logger.Info("database created") 63 64 64 - server := server.NewServer(cmd.String("port"), db, logger) 65 + server := server.NewServer(appContext, cmd.String("port"), db, logger) 65 66 66 67 if err := server.Start(); err != nil { 67 68 log.Fatal(err) 68 69 } 69 70 70 71 return nil 71 - } 72 + } 73 + 74 + // func migrate(db *sql.DB) { 75 + // _, err = db. 76 + // }
+4 -4
internal/server/handlers.go
··· 3 3 import "net/http" 4 4 5 5 func (s *Server) handleHomeRoute(w http.ResponseWriter, r *http.Request) { 6 - w.WriteHeader(http.StatusOK) 7 - w.Write([]byte("hello world")) 6 + w.WriteHeader(http.StatusOK) 7 + w.Write([]byte("hello world")) 8 8 } 9 9 10 10 func (s *Server) handleHealthCheckRoute(w http.ResponseWriter, r *http.Request) { 11 - w.WriteHeader(http.StatusOK); 11 + w.WriteHeader(http.StatusOK) 12 12 w.Write([]byte("reporting for duty")) 13 - } 13 + }
+29 -16
internal/server/server.go
··· 1 1 package server 2 2 3 3 import ( 4 + "context" 4 5 "database/sql" 5 6 "fmt" 6 7 "net/http" ··· 11 12 ) 12 13 13 14 type Server struct { 14 - addr string 15 - db *sql.DB 15 + ctx context.Context 16 + addr string 17 + db *sql.DB 16 18 logger *log.Logger 17 19 } 18 20 ··· 21 23 status int 22 24 } 23 25 24 - func NewServer(addr string, db *sql.DB, logger *log.Logger) *Server { 26 + func NewServer(ctx context.Context, addr string, db *sql.DB, logger *log.Logger) *Server { 25 27 return &Server{ 28 + ctx, 26 29 addr, 27 30 db, 28 31 logger, ··· 31 34 32 35 func (s *Server) Start() error { 33 36 router := mux.NewRouter() 37 + router.Use(s.requestLoggerMiddleware) 38 + router.Use(s.contextWithTimeoutMiddleware) 34 39 server := &http.Server{ 35 - Addr: s.addr, 40 + Addr: s.addr, 36 41 WriteTimeout: 5 * time.Second, 37 - ReadTimeout: 10 * time.Second, 38 - IdleTimeout: 30 * time.Second, 39 - Handler: requestLoggerMiddleware(s.logger, router), 42 + ReadTimeout: 10 * time.Second, 43 + IdleTimeout: 30 * time.Second, 44 + Handler: router, 40 45 } 41 46 42 47 router.HandleFunc("/healthcheck", s.handleHomeRoute) ··· 46 51 return server.ListenAndServe() 47 52 } 48 53 49 - func requestLoggerMiddleware(logger *log.Logger, next http.Handler) http.Handler { 54 + func (s *Server) contextWithTimeoutMiddleware(next http.Handler) http.Handler { 55 + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 56 + ctx, cancel := context.WithTimeout(r.Context(), time.Second*30) 57 + defer cancel() 58 + r = r.WithContext(ctx) 59 + next.ServeHTTP(w, r) 60 + }) 61 + } 62 + 63 + func (s *Server) requestLoggerMiddleware(next http.Handler) http.Handler { 50 64 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 51 65 recorder := statusRecorder{ 52 66 ResponseWriter: w, 53 - status: http.StatusOK, 67 + status: http.StatusOK, 54 68 } 55 69 56 70 next.ServeHTTP(recorder, r) 57 71 var ( 58 - ip = r.RemoteAddr 59 - method = r.Method 60 - url = r.URL.String() 61 - proto = r.Proto 72 + ip = r.RemoteAddr 73 + method = r.Method 74 + url = r.URL.String() 75 + proto = r.Proto 62 76 timestamp = time.Now().Format(time.RFC850) 63 77 ) 64 78 65 - 66 - logger.Info(fmt.Sprintf("%s [%s] %s %s %s %d", ip, timestamp, method, url, proto, recorder.status)) 79 + s.logger.Info(fmt.Sprintf("%s [%s] %s %s %s %d", ip, timestamp, method, url, proto, recorder.status)) 67 80 }) 68 - } 81 + }
+2 -2
justfile
··· 5 5 go run cmd/*.go start 6 6 7 7 build: 8 - CGO_ENABLED=0 go build -o build/pallet ./cmd/main.go 8 + CGO_ENABLED=1 go build -o build/pallet ./cmd/main.go 9 9 10 10 clean: 11 - rm -rf build 11 + rm -rf build