Compare commits
No commits in common. "main" and "v1.9.1" have entirely different histories.
8 changed files with 24 additions and 137 deletions
|
@ -1,4 +1,4 @@
|
||||||
package webutils
|
package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package webutils
|
package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package webutils
|
package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
@ -10,63 +9,8 @@ import (
|
||||||
// The error will have the given return code `code`
|
// The error will have the given return code `code`
|
||||||
// and a json encoded body with the field "id" set to `errId`
|
// and a json encoded body with the field "id" set to `errId`
|
||||||
// and a field "message" set to the `message`
|
// and a field "message" set to the `message`
|
||||||
//
|
|
||||||
// Deprecated: Use ProblemDetails or ProblemDetailsStatusOnly instead
|
|
||||||
func HttpErr(w http.ResponseWriter, errId int, message string, code int) {
|
func HttpErr(w http.ResponseWriter, errId int, message string, code int) {
|
||||||
w.WriteHeader(code)
|
w.WriteHeader(code)
|
||||||
w.Header().Add("Content-Type", "application/json")
|
w.Header().Add("Content-Type", "application/json")
|
||||||
fmt.Fprintf(w, "{\"id\": %d, \"message\": \"%s\"}", errId, message)
|
fmt.Fprintf(w, "{\"id\": %d, \"message\": \"%s\"}", errId, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write an RFC 9457 compliant problem details response
|
|
||||||
// If details is not nil, it will be included.
|
|
||||||
// If extras is not nil, each key-value pair will be included at
|
|
||||||
// the root layer.
|
|
||||||
// Keys in extras that would overwrite the default elements will be ignored.
|
|
||||||
// Those would be "type", "status", "title" and "detail"
|
|
||||||
func ProblemDetails(
|
|
||||||
w http.ResponseWriter,
|
|
||||||
statusCode int,
|
|
||||||
errorType string,
|
|
||||||
errorTitle string,
|
|
||||||
details *string,
|
|
||||||
extras map[string]any,
|
|
||||||
) {
|
|
||||||
w.Header().Add("Content-Type", "application/problem+json")
|
|
||||||
w.WriteHeader(statusCode)
|
|
||||||
data := map[string]any{
|
|
||||||
"type": errorType,
|
|
||||||
"status": statusCode,
|
|
||||||
"title": errorTitle,
|
|
||||||
}
|
|
||||||
if details != nil {
|
|
||||||
data["detail"] = *details
|
|
||||||
}
|
|
||||||
if extras != nil {
|
|
||||||
for k, v := range extras {
|
|
||||||
if _, ok := data[k]; ok {
|
|
||||||
// Don't overwrite default fields
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
data[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
enc := json.NewEncoder(w)
|
|
||||||
enc.Encode(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write a simple problem details response.
|
|
||||||
// It only provides the status code, as defined in RFC 9457, section 4.2.1
|
|
||||||
func ProblemDetailsStatusOnly(w http.ResponseWriter, statusCode int) {
|
|
||||||
w.Header().Add("Content-Type", "application/problem+json")
|
|
||||||
w.WriteHeader(statusCode)
|
|
||||||
data := map[string]any{
|
|
||||||
"type": "about:blank",
|
|
||||||
"title": http.StatusText(statusCode),
|
|
||||||
"status": statusCode,
|
|
||||||
"reference": "RFC 9457",
|
|
||||||
}
|
|
||||||
|
|
||||||
enc := json.NewEncoder(w)
|
|
||||||
enc.Encode(data)
|
|
||||||
}
|
|
||||||
|
|
17
http/json.go
17
http/json.go
|
@ -1,17 +0,0 @@
|
||||||
package webutils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
func SendJson(w http.ResponseWriter, data any) error {
|
|
||||||
encoded, err := json.Marshal(data)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
w.Header().Add("Content-Type", "application/json")
|
|
||||||
fmt.Fprint(w, string(encoded))
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
package webutils
|
package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -9,33 +9,6 @@ import (
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
func BuildLoggingMiddleware(extras map[string]string) HandlerBuilder {
|
|
||||||
return func(h http.Handler) http.Handler {
|
|
||||||
return ChainMiddlewares(h,
|
|
||||||
hlog.NewHandler(log.Logger),
|
|
||||||
hlog.AccessHandler(func(r *http.Request, status, size int, duration time.Duration) {
|
|
||||||
if strings.HasPrefix(r.URL.Path, "/assets") {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
logger := hlog.FromRequest(r).Info().
|
|
||||||
Str("method", r.Method).
|
|
||||||
Stringer("url", r.URL).
|
|
||||||
Int("status", status).
|
|
||||||
Int("size", size).
|
|
||||||
Dur("duration", duration)
|
|
||||||
for k, v := range extras {
|
|
||||||
logger = logger.Str(k, v)
|
|
||||||
}
|
|
||||||
logger.Send()
|
|
||||||
}),
|
|
||||||
hlog.RemoteAddrHandler("ip"),
|
|
||||||
hlog.UserAgentHandler("user_agent"),
|
|
||||||
hlog.RefererHandler("referer"),
|
|
||||||
hlog.RequestIDHandler("req_id", "Request-Id"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func LoggingMiddleware(handler http.Handler) http.Handler {
|
func LoggingMiddleware(handler http.Handler) http.Handler {
|
||||||
return ChainMiddlewares(handler,
|
return ChainMiddlewares(handler,
|
||||||
hlog.NewHandler(log.Logger),
|
hlog.NewHandler(log.Logger),
|
||||||
|
|
13
math/math.go
13
math/math.go
|
@ -1,13 +0,0 @@
|
||||||
package mathutils
|
|
||||||
|
|
||||||
type SignedNumber interface {
|
|
||||||
~int | ~int8 | ~int16 | ~int32 | ~int64 | ~float32 | ~float64
|
|
||||||
}
|
|
||||||
|
|
||||||
func Abs[T SignedNumber](num T) T {
|
|
||||||
if num > 0 {
|
|
||||||
return num
|
|
||||||
} else {
|
|
||||||
return num * -1
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -8,6 +8,8 @@ import (
|
||||||
|
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
|
|
||||||
|
"git.mstar.dev/mstar/goutils/logrotate"
|
||||||
)
|
)
|
||||||
|
|
||||||
var cliFlagLogLevel = "info"
|
var cliFlagLogLevel = "info"
|
||||||
|
@ -28,12 +30,13 @@ func SetupFlags() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure logging. Utilises the flags setup in [SetupFlags].
|
// If logfileName != nil, that file will be used for writing logs to.
|
||||||
// If logWriter is not nil, will also write logs, as json objects,
|
// Additionally, this function will then return another function
|
||||||
// to the given writer
|
// for rotating the log file
|
||||||
func ConfigureLogging(logWriter io.Writer) {
|
func ConfigureLogging(logfileName *string) func() error {
|
||||||
configOutputs(logWriter)
|
rotateFunc := configOutputs(logfileName)
|
||||||
configLevel()
|
configLevel()
|
||||||
|
return rotateFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
func configLevel() {
|
func configLevel() {
|
||||||
|
@ -56,14 +59,20 @@ func configLevel() {
|
||||||
log.Info().Str("new-level", cliFlagLogLevel).Msg("New logging level set")
|
log.Info().Str("new-level", cliFlagLogLevel).Msg("New logging level set")
|
||||||
}
|
}
|
||||||
|
|
||||||
func configOutputs(logWriter io.Writer) {
|
func configOutputs(logfileName *string) (rotateLogs func() error) {
|
||||||
console := zerolog.ConsoleWriter{Out: os.Stderr}
|
rotateLogs = func() error { return nil }
|
||||||
extraLogWriters := []io.Writer{console}
|
extraLogWriters := []io.Writer{}
|
||||||
if logWriter != nil {
|
if logfileName != nil {
|
||||||
extraLogWriters = append(extraLogWriters, logWriter)
|
logfile, err := logrotate.New(*logfileName)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("Failed to open log file")
|
||||||
|
}
|
||||||
|
extraLogWriters = append(extraLogWriters, logfile)
|
||||||
|
rotateLogs = logfile.Rotate
|
||||||
}
|
}
|
||||||
if !cliFlagLogJson {
|
if !cliFlagLogJson {
|
||||||
log.Logger = zerolog.New(zerolog.MultiLevelWriter(extraLogWriters...)).
|
console := zerolog.ConsoleWriter{Out: os.Stderr}
|
||||||
|
log.Logger = zerolog.New(zerolog.MultiLevelWriter(append([]io.Writer{console}, extraLogWriters...)...)).
|
||||||
With().
|
With().
|
||||||
Timestamp().
|
Timestamp().
|
||||||
Logger()
|
Logger()
|
||||||
|
|
|
@ -81,15 +81,6 @@ func Contains[T comparable](a []T, b T) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func ContainsFunc[T any](a []T, f func(t T) bool) bool {
|
|
||||||
for _, v := range a {
|
|
||||||
if f(v) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func Compact[T any](a []T, compactor func(acc T, next T) T) T {
|
func Compact[T any](a []T, compactor func(acc T, next T) T) T {
|
||||||
var acc T
|
var acc T
|
||||||
for _, v := range a {
|
for _, v := range a {
|
||||||
|
|
Loading…
Reference in a new issue