package alertmanager import ( "context" "io" "net/http" "strconv" "time" "github.com/gorilla/mux" "go.signoz.io/signoz/pkg/errors" "go.signoz.io/signoz/pkg/http/render" "go.signoz.io/signoz/pkg/types/alertmanagertypes" "go.signoz.io/signoz/pkg/types/authtypes" ) type API struct { alertmanager Alertmanager } func NewAPI(alertmanager Alertmanager) *API { return &API{ alertmanager: alertmanager, } } func (api *API) GetAlerts(rw http.ResponseWriter, req *http.Request) { ctx, cancel := context.WithTimeout(req.Context(), 30*time.Second) defer cancel() claims, ok := authtypes.ClaimsFromContext(ctx) if !ok { render.Error(rw, errors.Newf(errors.TypeUnauthenticated, errors.CodeUnauthenticated, "unauthenticated")) return } params, err := alertmanagertypes.NewGettableAlertsParams(req) if err != nil { render.Error(rw, err) return } alerts, err := api.alertmanager.GetAlerts(ctx, claims.OrgID, params) if err != nil { render.Error(rw, err) return } render.Success(rw, http.StatusOK, alerts) } func (api *API) TestReceiver(rw http.ResponseWriter, req *http.Request) { ctx, cancel := context.WithTimeout(req.Context(), 30*time.Second) defer cancel() claims, ok := authtypes.ClaimsFromContext(ctx) if !ok { render.Error(rw, errors.Newf(errors.TypeUnauthenticated, errors.CodeUnauthenticated, "unauthenticated")) return } body, err := io.ReadAll(req.Body) if err != nil { render.Error(rw, err) return } defer req.Body.Close() //nolint:errcheck receiver, err := alertmanagertypes.NewReceiver(string(body)) if err != nil { render.Error(rw, err) return } err = api.alertmanager.TestReceiver(ctx, claims.OrgID, receiver) if err != nil { render.Error(rw, err) return } render.Success(rw, http.StatusNoContent, nil) } func (api *API) ListChannels(rw http.ResponseWriter, req *http.Request) { ctx, cancel := context.WithTimeout(req.Context(), 30*time.Second) defer cancel() claims, ok := authtypes.ClaimsFromContext(ctx) if !ok { render.Error(rw, errors.Newf(errors.TypeUnauthenticated, errors.CodeUnauthenticated, "unauthenticated")) return } channels, err := api.alertmanager.ListChannels(ctx, claims.OrgID) if err != nil { render.Error(rw, err) return } // This ensures that the UI receives an empty array instead of null if len(channels) == 0 { channels = make([]*alertmanagertypes.Channel, 0) } render.Success(rw, http.StatusOK, channels) } func (api *API) ListAllChannels(rw http.ResponseWriter, req *http.Request) { ctx, cancel := context.WithTimeout(req.Context(), 30*time.Second) defer cancel() channels, err := api.alertmanager.ListAllChannels(ctx) if err != nil { render.Error(rw, err) return } render.Success(rw, http.StatusOK, channels) } func (api *API) GetChannelByID(rw http.ResponseWriter, req *http.Request) { ctx, cancel := context.WithTimeout(req.Context(), 30*time.Second) defer cancel() claims, ok := authtypes.ClaimsFromContext(ctx) if !ok { render.Error(rw, errors.Newf(errors.TypeUnauthenticated, errors.CodeUnauthenticated, "unauthenticated")) return } vars := mux.Vars(req) if vars == nil { render.Error(rw, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "id is required in path")) return } idString, ok := vars["id"] if !ok { render.Error(rw, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "id is required in path")) return } id, err := strconv.Atoi(idString) if err != nil { render.Error(rw, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "id is not a valid integer")) return } channel, err := api.alertmanager.GetChannelByID(ctx, claims.OrgID, id) if err != nil { render.Error(rw, err) return } render.Success(rw, http.StatusOK, channel) } func (api *API) UpdateChannelByID(rw http.ResponseWriter, req *http.Request) { ctx, cancel := context.WithTimeout(req.Context(), 30*time.Second) defer cancel() claims, ok := authtypes.ClaimsFromContext(ctx) if !ok { render.Error(rw, errors.Newf(errors.TypeUnauthenticated, errors.CodeUnauthenticated, "unauthenticated")) return } vars := mux.Vars(req) if vars == nil { render.Error(rw, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "id is required in path")) return } idString, ok := vars["id"] if !ok { render.Error(rw, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "id is required in path")) return } id, err := strconv.Atoi(idString) if err != nil { render.Error(rw, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "id is not a valid integer")) return } body, err := io.ReadAll(req.Body) if err != nil { render.Error(rw, err) return } defer req.Body.Close() //nolint:errcheck receiver, err := alertmanagertypes.NewReceiver(string(body)) if err != nil { render.Error(rw, err) return } err = api.alertmanager.UpdateChannelByReceiverAndID(ctx, claims.OrgID, receiver, id) if err != nil { render.Error(rw, err) return } render.Success(rw, http.StatusNoContent, nil) } func (api *API) DeleteChannelByID(rw http.ResponseWriter, req *http.Request) { ctx, cancel := context.WithTimeout(req.Context(), 30*time.Second) defer cancel() claims, ok := authtypes.ClaimsFromContext(ctx) if !ok { render.Error(rw, errors.Newf(errors.TypeUnauthenticated, errors.CodeUnauthenticated, "unauthenticated")) return } vars := mux.Vars(req) if vars == nil { render.Error(rw, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "id is required in path")) return } idString, ok := vars["id"] if !ok { render.Error(rw, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "id is required in path")) return } id, err := strconv.Atoi(idString) if err != nil { render.Error(rw, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "id is not a valid integer")) return } err = api.alertmanager.DeleteChannelByID(ctx, claims.OrgID, id) if err != nil { render.Error(rw, err) return } render.Success(rw, http.StatusNoContent, nil) } func (api *API) CreateChannel(rw http.ResponseWriter, req *http.Request) { ctx, cancel := context.WithTimeout(req.Context(), 30*time.Second) defer cancel() claims, ok := authtypes.ClaimsFromContext(ctx) if !ok { render.Error(rw, errors.Newf(errors.TypeUnauthenticated, errors.CodeUnauthenticated, "unauthenticated")) return } body, err := io.ReadAll(req.Body) if err != nil { render.Error(rw, err) return } defer req.Body.Close() //nolint:errcheck receiver, err := alertmanagertypes.NewReceiver(string(body)) if err != nil { render.Error(rw, err) return } err = api.alertmanager.CreateChannel(ctx, claims.OrgID, receiver) if err != nil { render.Error(rw, err) return } render.Success(rw, http.StatusNoContent, nil) }