package main import ( "context" "io" "net/http" "os" "os/signal" "codeberg.org/u1f320/pronouns.cc/backend/log" "codeberg.org/u1f320/pronouns.cc/backend/server" "github.com/go-chi/render" _ "github.com/joho/godotenv/autoload" ) func main() { port := ":" + os.Getenv("PORT") s, err := server.New() if err != nil { log.Fatalf("Error creating server: %v", err) } // set render.Decode to a custom one that checks content length render.Decode = decode // mount api routes mountRoutes(s) e := make(chan error) // run server in another goroutine (for gracefully shutting down, see below) go func() { e <- http.ListenAndServe(port, s.Router) }() ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt) defer stop() log.Infof("API server running at %v!", port) select { case <-ctx.Done(): log.Info("Interrupt signal received, shutting down...") s.DB.Close() return case err := <-e: log.Fatalf("Error running server: %v", err) } } const MaxContentLength = 2 * 1024 * 1024 // decode is a custom render.Decode function that makes sure the request doesn't exceed 2 megabytes. func decode(r *http.Request, v any) error { if r.ContentLength > MaxContentLength { return server.APIError{ Code: server.ErrRequestTooBig, } } // this is copied from render.Decode to replace r.Body with an io.LimitedReader var err error lr := io.LimitReader(r.Body, MaxContentLength) switch render.GetRequestContentType(r) { case render.ContentTypeJSON: err = render.DecodeJSON(lr, v) default: err = server.APIError{ Code: server.ErrBadRequest, } } return err }