Add connection webhook notifications (#392)
Also * Added decode of LoginStart message * Add metrics backend constants * Updated usage section * Documented MaxFrameLength
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/sirupsen/logrus"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// WebhookNotifier implements ConnectionNotifier by sending a POST request to a webhook URL.
|
||||
// The payload is a JSON object defined by WebhookNotifierPayload.
|
||||
type WebhookNotifier struct {
|
||||
url string
|
||||
requireUser bool
|
||||
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
const (
|
||||
WebhookEventConnecting = "connect"
|
||||
)
|
||||
|
||||
const (
|
||||
WebhookStatusMissingBackend = "missing-backend"
|
||||
WebhookStatusFailedBackendConnection = "failed-backend-connection"
|
||||
WebhookStatusSuccess = "success"
|
||||
)
|
||||
|
||||
type WebhookNotifierPayload struct {
|
||||
Event string `json:"event"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
Status string `json:"status"`
|
||||
Client *ClientInfo `json:"client"`
|
||||
Server string `json:"server"`
|
||||
PlayerInfo *PlayerInfo `json:"player,omitempty"`
|
||||
BackendHostPort string `json:"backend,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
func NewWebhookNotifier(url string, requireUser bool) *WebhookNotifier {
|
||||
|
||||
return &WebhookNotifier{
|
||||
url: url,
|
||||
requireUser: requireUser,
|
||||
client: &http.Client{
|
||||
Timeout: 30 * time.Second,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (w *WebhookNotifier) NotifyMissingBackend(ctx context.Context, clientAddr net.Addr, server string, playerInfo *PlayerInfo) error {
|
||||
if w.requireUser && playerInfo == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
payload := &WebhookNotifierPayload{
|
||||
Event: WebhookEventConnecting,
|
||||
Timestamp: time.Now(),
|
||||
Status: WebhookStatusMissingBackend,
|
||||
Client: ClientInfoFromAddr(clientAddr),
|
||||
Server: server,
|
||||
PlayerInfo: playerInfo,
|
||||
Error: "No backend found",
|
||||
}
|
||||
|
||||
return w.send(ctx, payload)
|
||||
}
|
||||
|
||||
func (w *WebhookNotifier) NotifyFailedBackendConnection(ctx context.Context, clientAddr net.Addr, server string,
|
||||
playerInfo *PlayerInfo, backendHostPort string, err error) error {
|
||||
if w.requireUser && playerInfo == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
payload := &WebhookNotifierPayload{
|
||||
Event: WebhookEventConnecting,
|
||||
Timestamp: time.Now(),
|
||||
Status: WebhookStatusFailedBackendConnection,
|
||||
Client: ClientInfoFromAddr(clientAddr),
|
||||
Server: server,
|
||||
PlayerInfo: playerInfo,
|
||||
BackendHostPort: backendHostPort,
|
||||
Error: err.Error(),
|
||||
}
|
||||
|
||||
return w.send(ctx, payload)
|
||||
}
|
||||
|
||||
func (w *WebhookNotifier) NotifyConnected(ctx context.Context, clientAddr net.Addr, serverAddress string, playerInfo *PlayerInfo, backendHostPort string) error {
|
||||
if w.requireUser && playerInfo == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
payload := &WebhookNotifierPayload{
|
||||
Event: WebhookEventConnecting,
|
||||
Timestamp: time.Now(),
|
||||
Status: WebhookStatusSuccess,
|
||||
Client: ClientInfoFromAddr(clientAddr),
|
||||
Server: serverAddress,
|
||||
PlayerInfo: playerInfo,
|
||||
BackendHostPort: backendHostPort,
|
||||
}
|
||||
|
||||
return w.send(ctx, payload)
|
||||
}
|
||||
|
||||
func (w *WebhookNotifier) send(ctx context.Context, payload *WebhookNotifierPayload) error {
|
||||
jsonPayload, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal webhook payload: %v", err)
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(
|
||||
ctx,
|
||||
http.MethodPost,
|
||||
w.url,
|
||||
bytes.NewBuffer(jsonPayload),
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create webhook request: %v", err)
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
go func() {
|
||||
resp, err := w.client.Do(req)
|
||||
if err != nil {
|
||||
// Handle error
|
||||
log.Printf("Failed to send webhook notification: %v", err)
|
||||
return
|
||||
}
|
||||
_ = resp.Body.Close()
|
||||
|
||||
if resp.StatusCode >= 400 {
|
||||
logrus.
|
||||
WithField("status", resp.StatusCode).
|
||||
Warn("webhook receiver responded with an error")
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user