mirror of
https://github.com/teamhanko/hanko.git
synced 2025-10-27 06:06:54 +08:00
256 lines
6.2 KiB
Go
256 lines
6.2 KiB
Go
package handler
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/gobuffalo/pop/v6"
|
|
"github.com/gofrs/uuid"
|
|
"github.com/labstack/echo/v4"
|
|
"github.com/teamhanko/hanko/backend/v2/config"
|
|
"github.com/teamhanko/hanko/backend/v2/dto/admin"
|
|
"github.com/teamhanko/hanko/backend/v2/persistence"
|
|
"github.com/teamhanko/hanko/backend/v2/persistence/models"
|
|
"github.com/teamhanko/hanko/backend/v2/webhooks"
|
|
"github.com/teamhanko/hanko/backend/v2/webhooks/events"
|
|
"net/http"
|
|
"time"
|
|
)
|
|
|
|
type WebhookHandler interface {
|
|
List(ctx echo.Context) error
|
|
Create(ctx echo.Context) error
|
|
Get(ctx echo.Context) error
|
|
Delete(ctx echo.Context) error
|
|
Update(ctx echo.Context) error
|
|
}
|
|
|
|
const (
|
|
uuidErrorFormat = "unable to create uuid: %w"
|
|
)
|
|
|
|
type webhookHandler struct {
|
|
cfg config.WebhookSettings
|
|
persister persistence.Persister
|
|
}
|
|
|
|
func NewWebhookHandler(cfg config.WebhookSettings, persister persistence.Persister) WebhookHandler {
|
|
return &webhookHandler{
|
|
cfg: cfg,
|
|
persister: persister,
|
|
}
|
|
}
|
|
|
|
func (w *webhookHandler) List(ctx echo.Context) error {
|
|
persister := w.persister.GetWebhookPersister(nil)
|
|
dbHooks, err := persister.List(true)
|
|
if err != nil {
|
|
ctx.Logger().Error(err)
|
|
return fmt.Errorf("failed to list users: %w", err)
|
|
}
|
|
|
|
listDto := admin.WebhookListResponseDto{
|
|
Database: dbHooks,
|
|
Config: w.cfg.Hooks,
|
|
}
|
|
|
|
return ctx.JSON(http.StatusOK, listDto)
|
|
}
|
|
|
|
func (w *webhookHandler) Create(ctx echo.Context) error {
|
|
var dto admin.CreateWebhookRequestDto
|
|
err := ctx.Bind(&dto)
|
|
if err != nil {
|
|
ctx.Logger().Error(err)
|
|
return echo.NewHTTPError(http.StatusBadRequest, err)
|
|
}
|
|
|
|
err = ctx.Validate(dto)
|
|
if err != nil {
|
|
ctx.Logger().Error(err)
|
|
return echo.NewHTTPError(http.StatusBadRequest, err)
|
|
}
|
|
|
|
now := time.Now()
|
|
|
|
newUuid, err := uuid.NewV4()
|
|
if err != nil {
|
|
ctx.Logger().Error(err)
|
|
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Errorf(uuidErrorFormat, err))
|
|
}
|
|
|
|
model := models.Webhook{
|
|
ID: newUuid,
|
|
Callback: dto.Callback,
|
|
Enabled: true,
|
|
Failures: 0,
|
|
ExpiresAt: now.Add(webhooks.WebhookExpireDuration), // 30 Days from now
|
|
WebhookEvents: nil,
|
|
CreatedAt: now,
|
|
UpdatedAt: now,
|
|
}
|
|
|
|
dbEvents, err := w.createWebhookEvents(dto.Events, model, now)
|
|
if err != nil {
|
|
ctx.Logger().Error(err)
|
|
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Errorf(uuidErrorFormat, err))
|
|
}
|
|
|
|
persister := w.persister.GetWebhookPersister(nil)
|
|
err = persister.Create(model, dbEvents)
|
|
if err != nil {
|
|
ctx.Logger().Error(err)
|
|
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Errorf("unable to save webhook: %w", err))
|
|
}
|
|
|
|
model.WebhookEvents = dbEvents
|
|
|
|
return ctx.JSON(http.StatusCreated, model)
|
|
}
|
|
|
|
func (w *webhookHandler) createWebhookEvents(evts events.Events, webhook models.Webhook, now time.Time) (models.WebhookEvents, error) {
|
|
eventList := make(models.WebhookEvents, 0)
|
|
for _, event := range evts {
|
|
newUuid, err := uuid.NewV4()
|
|
if err != nil {
|
|
return eventList, err
|
|
}
|
|
|
|
model := models.WebhookEvent{
|
|
ID: newUuid,
|
|
Webhook: &webhook,
|
|
Event: string(event),
|
|
CreatedAt: now,
|
|
UpdatedAt: now,
|
|
}
|
|
|
|
eventList = append(eventList, model)
|
|
}
|
|
|
|
return eventList, nil
|
|
}
|
|
|
|
func (w *webhookHandler) Get(ctx echo.Context) error {
|
|
var dto admin.GetWebhookRequestDto
|
|
err := ctx.Bind(&dto)
|
|
if err != nil {
|
|
ctx.Logger().Error(err)
|
|
return echo.NewHTTPError(http.StatusBadRequest, err)
|
|
}
|
|
|
|
err = ctx.Validate(dto)
|
|
if err != nil {
|
|
ctx.Logger().Error(err)
|
|
return echo.NewHTTPError(http.StatusBadRequest, err)
|
|
}
|
|
|
|
webhookId, _ := uuid.FromString(dto.ID)
|
|
webhook, err := w.getWebhook(webhookId, w.persister.GetWebhookPersister(nil))
|
|
if err != nil {
|
|
ctx.Logger().Error(err)
|
|
return err
|
|
}
|
|
|
|
return ctx.JSON(http.StatusOK, webhook)
|
|
}
|
|
|
|
func (w *webhookHandler) Delete(ctx echo.Context) error {
|
|
var dto admin.GetWebhookRequestDto
|
|
err := ctx.Bind(&dto)
|
|
if err != nil {
|
|
ctx.Logger().Error(err)
|
|
return echo.NewHTTPError(http.StatusBadRequest, err)
|
|
}
|
|
|
|
err = ctx.Validate(dto)
|
|
if err != nil {
|
|
ctx.Logger().Error(err)
|
|
return echo.NewHTTPError(http.StatusBadRequest, err)
|
|
}
|
|
|
|
persister := w.persister.GetWebhookPersister(nil)
|
|
|
|
webhookId, _ := uuid.FromString(dto.ID)
|
|
webhook, err := w.getWebhook(webhookId, persister)
|
|
if err != nil {
|
|
ctx.Logger().Error(err)
|
|
return err
|
|
}
|
|
|
|
err = persister.Delete(*webhook)
|
|
if err != nil {
|
|
ctx.Logger().Error(err)
|
|
return fmt.Errorf("unable to delete webhook from database: %w", err)
|
|
}
|
|
|
|
return ctx.NoContent(http.StatusNoContent)
|
|
}
|
|
|
|
func (w *webhookHandler) Update(ctx echo.Context) error {
|
|
var dto admin.UpdateWebhookRequestDto
|
|
err := ctx.Bind(&dto)
|
|
if err != nil {
|
|
ctx.Logger().Error(err)
|
|
return echo.NewHTTPError(http.StatusBadRequest, err)
|
|
}
|
|
|
|
err = ctx.Validate(dto)
|
|
if err != nil {
|
|
ctx.Logger().Error(err)
|
|
return echo.NewHTTPError(http.StatusBadRequest, err)
|
|
}
|
|
|
|
return w.persister.Transaction(func(tx *pop.Connection) error {
|
|
persister := w.persister.GetWebhookPersister(tx)
|
|
|
|
webhookId, _ := uuid.FromString(dto.ID)
|
|
|
|
webhook, err := w.getWebhook(webhookId, persister)
|
|
if err != nil {
|
|
ctx.Logger().Error(err)
|
|
return err
|
|
}
|
|
|
|
for _, event := range webhook.WebhookEvents {
|
|
err := persister.RemoveEvent(event)
|
|
if err != nil {
|
|
ctx.Logger().Error(err)
|
|
return fmt.Errorf("unable to delete event: %w", err)
|
|
}
|
|
}
|
|
|
|
now := time.Now()
|
|
dbEvents, err := w.createWebhookEvents(dto.Events, *webhook, now)
|
|
if err != nil {
|
|
ctx.Logger().Error(err)
|
|
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Errorf(uuidErrorFormat, err))
|
|
}
|
|
|
|
webhook.WebhookEvents = dbEvents
|
|
webhook.Callback = dto.Callback
|
|
webhook.UpdatedAt = now
|
|
webhook.Enabled = dto.Enabled
|
|
webhook.Failures = 0
|
|
webhook.ExpiresAt = now.Add(webhooks.WebhookExpireDuration)
|
|
|
|
err = persister.Update(*webhook)
|
|
if err != nil {
|
|
ctx.Logger().Error(err)
|
|
return fmt.Errorf("unable to update webhook: %w", err)
|
|
}
|
|
|
|
return ctx.JSON(http.StatusOK, webhook)
|
|
})
|
|
}
|
|
|
|
func (w *webhookHandler) getWebhook(id uuid.UUID, persister persistence.WebhookPersister) (*models.Webhook, error) {
|
|
webhook, err := persister.Get(id)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to fetch webhook from database: %w", err)
|
|
}
|
|
|
|
if webhook == nil {
|
|
return nil, echo.NewHTTPError(http.StatusNotFound, "unable to find webhook with id: %s", id.String())
|
|
}
|
|
|
|
return webhook, nil
|
|
}
|