pomme/internal/api/helpers.go

110 lines
2.5 KiB
Go

package api
import (
"context"
"fmt"
"net/http"
"time"
"dns.froth.zone/pomme/internal"
"dns.froth.zone/pomme/internal/db"
"github.com/go-chi/httplog"
"github.com/go-chi/render"
"gorm.io/gorm"
)
// setDBMiddleware is the http Handler func for the GORM middleware with context.
func setDBMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var (
pommeDB *gorm.DB
ok bool
)
c, err := internal.ReadConfig()
if err != nil {
logger := newResponder(Response[any]{
Message: "No config file defined",
Err: err.Error(),
})
logger.writeLogEntry()
}
switch r.Header.Get("User-Agent") {
case "pomme-api-test-slave":
pommeDB, err, ok = db.InitDb(c.TestDB)
default:
pommeDB, err, ok = db.InitDb(c.DB)
}
if err != nil && !ok {
logger := newResponder(Response[any]{
Message: "Error initializing DB",
Err: err.Error(),
})
logger.writeLogEntry()
err = internal.SysKill()
if err != nil {
panic("this should not happen.")
}
}
timeoutContext, cancelContext := context.WithTimeout(context.Background(), time.Second)
ctx := context.WithValue(r.Context(), keyPrincipalContextID, pommeDB.WithContext(timeoutContext))
defer cancelContext()
next.ServeHTTP(w, r.WithContext(ctx))
})
}
// apiError sends unsuccessful API requests back to the user with the appropriate information.
func (e *Response[T]) apiError(w http.ResponseWriter, r *http.Request) {
v := map[string]any{
"message": e.Message,
"status": e.Status,
"realm": e.Realm,
"error": e.Err,
}
w.Header().Set("X-Content-Type-Options", "nosniff")
w.Header().Set("Content-Type", "application/json; charset=utf-8")
switch v["realm"] {
default:
w.Header().Add("WWW-Authenticate", fmt.Sprintf(`realm="%v"`, e.Realm))
delete(v, "realm")
fallthrough
case nil:
w.Header().Add("API Error", v["message"].(string))
}
w.WriteHeader(v["status"].(int))
delete(v, "error")
delete(v, "status")
delete(v, "realm")
render.JSON(w, r, v)
}
// writeLogEntry takes a response struct and writes info and error level logs
// todo: make it write to file maybe
func (e *Response[T]) writeLogEntry() {
v := map[string]any{
"message": e.Message,
"error": e.Err,
}
logger := httplog.NewLogger("Pomme", httplog.Options{
JSON: true,
})
switch v["error"] {
default:
logger.Error().Msg(v["error"].(string))
fallthrough
case nil:
logger.Info().Msg(v["message"].(string))
}
}