fog/cmd/api/api.go
2024-12-30 10:02:01 +00:00

113 lines
2.3 KiB
Go

package main
import (
"context"
"encoding/json"
"log"
"net/http"
"path"
"strings"
"time"
"midnadimple.com/fog/internal/store"
)
type application struct {
config config
store store.Storage
v1 *v1Handler
}
type config struct {
addr string
db dbConfig
}
type dbConfig struct {
addr string
maxOpenConns int
maxIdleConns int
maxIdleTime time.Duration
}
func (h *application) ServeHTTP(w http.ResponseWriter, r *http.Request) {
wWrapper := &wrappedResponseWriter{w, http.StatusOK}
w = wWrapper
// fullpath middleware
ctx := context.WithValue(r.Context(), "fullPath", r.URL.Path)
// logger middleware
startTime := time.Now()
defer func() {
log.Printf("request at %s, responded with status %d in %dms", ctx.Value("fullPath"), wWrapper.statusCode, time.Since(startTime).Milliseconds())
}()
// recoverer middleware
defer func() {
err := recover()
if err != nil {
log.Println(err)
jsonBody, _ := json.Marshal(map[string]string{
"error": "There was an internal server error",
})
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusInternalServerError)
w.Write(jsonBody)
}
}()
// timeout middleware
ctx, cancel := context.WithTimeout(ctx, time.Minute)
defer func() {
cancel()
if ctx.Err() == context.DeadlineExceeded {
w.WriteHeader(http.StatusGatewayTimeout)
}
}()
r = r.WithContext(ctx)
var head string
head, r.URL.Path = shiftPath(r.URL.Path)
switch head {
case "v1":
h.v1.ServeHTTP(w, r)
default:
http.Error(w, "Not Found", http.StatusNotFound)
}
}
func (app *application) run() error {
app.v1 = &v1Handler{
healthCheck: &healthCheckHandler{},
}
srv := &http.Server{
Addr: app.config.addr,
Handler: app,
WriteTimeout: time.Second * 30,
ReadTimeout: time.Second * 10,
IdleTimeout: time.Minute,
}
log.Printf("server has started at %s", app.config.addr)
return srv.ListenAndServe()
}
// ShiftPath splits off the first component of p, which will be cleaned of
// relative components before processing. head will never contain a slash and
// tail will always be a rooted path without trailing slash.
func shiftPath(p string) (head, tail string) {
p = path.Clean("/" + p)
i := strings.Index(p[1:], "/") + 1
if i <= 0 {
return p[1:], "/"
}
return p[1:i], p[i:]
}