2023-03-15 10:04:48 +01:00
|
|
|
package backend
|
2022-05-02 17:19:37 +02:00
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2022-09-20 13:02:48 +02:00
|
|
|
"io"
|
2022-05-02 17:19:37 +02:00
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"os/signal"
|
|
|
|
|
2022-05-14 16:52:08 +02:00
|
|
|
"codeberg.org/u1f320/pronouns.cc/backend/log"
|
|
|
|
"codeberg.org/u1f320/pronouns.cc/backend/server"
|
2022-05-02 17:19:37 +02:00
|
|
|
|
2023-05-11 00:59:40 +02:00
|
|
|
"github.com/davidbyttow/govips/v2/vips"
|
2022-09-20 13:02:48 +02:00
|
|
|
"github.com/go-chi/render"
|
2022-05-12 16:41:32 +02:00
|
|
|
_ "github.com/joho/godotenv/autoload"
|
2023-03-15 10:04:48 +01:00
|
|
|
"github.com/urfave/cli/v2"
|
2022-05-02 17:19:37 +02:00
|
|
|
)
|
|
|
|
|
2023-03-15 10:04:48 +01:00
|
|
|
var Command = &cli.Command{
|
|
|
|
Name: "web",
|
|
|
|
Usage: "Run the API server",
|
|
|
|
Action: run,
|
|
|
|
}
|
|
|
|
|
|
|
|
func run(c *cli.Context) error {
|
2023-05-22 00:36:21 +02:00
|
|
|
// set vips log level to WARN, else it will spam logs on info level
|
|
|
|
vips.LoggingSettings(nil, vips.LogLevelWarning)
|
|
|
|
|
2023-05-11 00:59:40 +02:00
|
|
|
vips.Startup(nil)
|
|
|
|
defer vips.Shutdown()
|
|
|
|
|
2022-05-02 17:19:37 +02:00
|
|
|
port := ":" + os.Getenv("PORT")
|
|
|
|
|
|
|
|
s, err := server.New()
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("Error creating server: %v", err)
|
2023-03-15 10:04:48 +01:00
|
|
|
return nil
|
2022-05-02 17:19:37 +02:00
|
|
|
}
|
|
|
|
|
2022-09-20 13:02:48 +02:00
|
|
|
// set render.Decode to a custom one that checks content length
|
|
|
|
render.Decode = decode
|
|
|
|
|
2022-05-02 17:19:37 +02:00
|
|
|
// mount api routes
|
|
|
|
mountRoutes(s)
|
|
|
|
|
|
|
|
e := make(chan error)
|
|
|
|
|
|
|
|
// run server in another goroutine (for gracefully shutting down, see below)
|
|
|
|
go func() {
|
2022-07-11 22:35:20 +02:00
|
|
|
e <- http.ListenAndServe(port, s.Router)
|
2022-05-02 17:19:37 +02:00
|
|
|
}()
|
|
|
|
|
|
|
|
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()
|
2023-03-15 10:04:48 +01:00
|
|
|
return nil
|
2022-05-02 17:19:37 +02:00
|
|
|
case err := <-e:
|
|
|
|
log.Fatalf("Error running server: %v", err)
|
|
|
|
}
|
2023-03-15 10:04:48 +01:00
|
|
|
|
|
|
|
return nil
|
2022-05-02 17:19:37 +02:00
|
|
|
}
|
2022-09-20 13:02:48 +02:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|