diff --git a/cmd/go-matrix-webhook/cmd.go b/cmd/go-matrix-webhook/cmd.go index 78856b0..dcdf776 100644 --- a/cmd/go-matrix-webhook/cmd.go +++ b/cmd/go-matrix-webhook/cmd.go @@ -4,7 +4,6 @@ import ( "fmt" "net/http" - "github.com/gorilla/mux" "github.com/mazzz1y/go-matrix-webhook/internal/handler" "github.com/mazzz1y/go-matrix-webhook/internal/matrix" "github.com/rs/zerolog/log" @@ -28,12 +27,9 @@ func run(c *cli.Context) error { if err != nil { panic(err) } - h := handler.NewHandler(*m, secretHeader) - - router := mux.NewRouter().StrictSlash(true) - router.HandleFunc(listenPath, h).Methods("POST") + http.HandleFunc(listenPath, handler.NewHandler(*m, secretHeader)) listen := fmt.Sprintf("%s:%d", listenAddr, listenPort) log.Info().Err(err).Msgf("listen on: %s", listen) - return http.ListenAndServe(listen, router) + return http.ListenAndServe(listen, nil) } diff --git a/cmd/go-matrix-webhook/main.go b/cmd/go-matrix-webhook/main.go index 1c2f82b..a089101 100644 --- a/cmd/go-matrix-webhook/main.go +++ b/cmd/go-matrix-webhook/main.go @@ -36,10 +36,9 @@ func main() { Value: "/", }, &cli.StringFlag{ - Name: "secret-header", - Usage: "secret header", - EnvVars: []string{"SECRET_HEADER"}, - Required: true, + Name: "secret-header", + Usage: "secret header", + EnvVars: []string{"SECRET_HEADER"}, }, &cli.StringFlag{ Name: "matrix-access-token", diff --git a/go.mod b/go.mod index bb877a8..65aa89f 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/mazzz1y/go-matrix-webhook go 1.20 require ( - github.com/gorilla/mux v1.8.0 github.com/rs/zerolog v1.29.1 github.com/urfave/cli/v2 v2.25.1 maunium.net/go/mautrix v0.15.1 diff --git a/go.sum b/go.sum index 9c0f624..b382f81 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,6 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHH github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= diff --git a/internal/handler/handler.go b/internal/handler/handler.go index ce4615b..d2cbbc5 100644 --- a/internal/handler/handler.go +++ b/internal/handler/handler.go @@ -3,10 +3,13 @@ package handler import ( "encoding/json" "errors" + "io" + "net" "net/http" + "strings" "github.com/mazzz1y/go-matrix-webhook/internal/matrix" - "github.com/rs/zerolog/log" + zerolog "github.com/rs/zerolog/log" ) type WebhookPayload struct { @@ -19,54 +22,90 @@ type ResponseData struct { Message string `json:"message"` } -func NewHandler(m matrix.Matrix, s string) func(http.ResponseWriter, *http.Request) { +const ( + successSent = "message sent" + errInvalidSecret = "invalid secret header" + errParseBodyError = "parse body error" + errJoinRoomError = "join room error" + errSendMessageError = "send message error" + errEmptyMessage = "empty message" +) + +func NewHandler(m matrix.Matrix, secret string) func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { - if r.Header.Get("X-Secret") != s { - response(w, http.StatusBadRequest, "invalid secret header") + secretHeader := r.Header.Get("X-Secret") + userHeader := r.Header.Get("X-Forwarded-User") + reqIP := getIP(r) + + if secret != "" && secretHeader != secret { + sendResponse(w, http.StatusBadRequest, errInvalidSecret) return } - payload, err := parsePayload(r) + log := zerolog.With().Str("ip", reqIP).Str("path", r.URL.Path).Logger() + if userHeader != "" { + log = log.With().Str("user", userHeader).Logger() + } + + payload, err := parsePayload(r.Body) if err != nil { - log.Error().Err(err).Msg("") - response(w, http.StatusBadRequest, "parse body error") + log.Error().Err(err).Msg(errParseBodyError) + sendResponse(w, http.StatusBadRequest, errParseBodyError) return } err = m.JoinRoom(payload.RoomID) if err != nil { - log.Error().Str("room_id", payload.RoomID).Err(err).Msg("") - response(w, http.StatusInternalServerError, "join room error") + log.Error().Str("room_id", payload.RoomID).Err(err).Msg(errJoinRoomError) + sendResponse(w, http.StatusInternalServerError, errJoinRoomError) return } err = m.SendMessage(payload.RoomID, payload.Message) if err != nil { - log.Error().Str("room_id", payload.RoomID).Err(err).Msg("") - response(w, http.StatusOK, "send message error") + log.Error().Str("room_id", payload.RoomID).Err(err).Msg(errSendMessageError) + sendResponse(w, http.StatusOK, errSendMessageError) return } - log.Debug().Str("room_id", payload.RoomID).Msg("message sent") - response(w, http.StatusOK, "") + log.Debug().Str("room_id", payload.RoomID).Msg(successSent) + sendResponse(w, http.StatusOK, "") } } -func parsePayload(r *http.Request) (*WebhookPayload, error) { +func parsePayload(body io.ReadCloser) (*WebhookPayload, error) { var payload WebhookPayload - err := json.NewDecoder(r.Body).Decode(&payload) + err := json.NewDecoder(body).Decode(&payload) if err != nil { return nil, err } if payload.Message == "" { - return nil, errors.New("empty message") + return nil, errors.New(errEmptyMessage) } return &payload, nil } -func response(w http.ResponseWriter, code int, msg string) { +func getIP(r *http.Request) string { + if xff := r.Header.Get("X-Forwarded-For"); xff != "" { + ipParts := strings.Split(xff, ",") + return strings.TrimSpace(ipParts[0]) + } + + if xri := r.Header.Get("X-Real-IP"); xri != "" { + return xri + } + + if cf := r.Header.Get("CF-Connecting-IP"); cf != "" { + return cf + } + + ip, _, _ := net.SplitHostPort(r.RemoteAddr) + return ip +} + +func sendResponse(w http.ResponseWriter, code int, msg string) { if msg == "" { msg = http.StatusText(code) }