mood/inspo boards
1package server
2
3import (
4 "context"
5 "database/sql"
6 "fmt"
7 "net/http"
8 "time"
9
10 "github.com/charmbracelet/log"
11 "github.com/gorilla/mux"
12)
13
14type Server struct {
15 addr string
16 db *sql.DB
17 logger *log.Logger
18}
19
20type statusRecorder struct {
21 http.ResponseWriter
22 status int
23}
24
25func NewServer(addr string, db *sql.DB, logger *log.Logger) *Server {
26 return &Server{
27 addr,
28 db,
29 logger,
30 }
31}
32
33func (s *Server) Start() error {
34 router := mux.NewRouter()
35 router.Use(s.requestLoggerMiddleware)
36 router.Use(s.contextWithTimeoutMiddleware)
37 server := &http.Server{
38 Addr: s.addr,
39 WriteTimeout: 5 * time.Second,
40 ReadTimeout: 10 * time.Second,
41 IdleTimeout: 30 * time.Second,
42 Handler: router,
43 }
44
45 router.HandleFunc("/healthcheck", s.handleHomeRoute)
46 router.HandleFunc("/", s.handleHealthCheckRoute)
47
48 s.logger.Info(fmt.Sprintf("app server started at 0.0.0.0%s", s.addr))
49 return server.ListenAndServe()
50}
51
52func (s *Server) contextWithTimeoutMiddleware(next http.Handler) http.Handler {
53 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
54 ctx, cancel := context.WithTimeout(r.Context(), time.Second*30)
55 defer cancel()
56 r = r.WithContext(ctx)
57 next.ServeHTTP(w, r)
58 })
59}
60
61func (s *Server) requestLoggerMiddleware(next http.Handler) http.Handler {
62 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
63 recorder := statusRecorder{
64 ResponseWriter: w,
65 status: http.StatusOK,
66 }
67
68 next.ServeHTTP(recorder, r)
69 var (
70 ip = r.RemoteAddr
71 method = r.Method
72 url = r.URL.String()
73 proto = r.Proto
74 timestamp = time.Now().Format(time.RFC850)
75 )
76
77 s.logger.Info(fmt.Sprintf("%s [%s] %s %s %s %d", ip, timestamp, method, url, proto, recorder.status))
78 })
79}