mood/inspo boards

middleware refactors

besaid.zone 92eb6e01 477d6a23

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