mirror of
https://github.com/makayabou/asg-server.git
synced 2026-05-02 17:43:36 +02:00
[webhooks] model, DTO, repository, service, handler
This commit is contained in:
parent
ebb97a8238
commit
c0ce84e89f
@ -57,3 +57,23 @@ Authorization: Basic {{localCredentials}}
|
||||
###
|
||||
GET {{localUrl}}/message/8GN2Pz-fzu73NL3398ROE HTTP/1.1
|
||||
Authorization: Basic {{localCredentials}}
|
||||
|
||||
###
|
||||
GET {{localUrl}}/webhooks HTTP/1.1
|
||||
Authorization: Basic {{localCredentials}}
|
||||
|
||||
###
|
||||
POST {{localUrl}}/webhooks HTTP/1.1
|
||||
Authorization: Basic {{localCredentials}}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"id": "LreFUt-Z3sSq0JufY9uWB",
|
||||
"url": "https://webhook.site/280a6655-eb68-40b9-b857-af5be37c5303",
|
||||
"event": "sms:received"
|
||||
}
|
||||
|
||||
###
|
||||
DELETE {{localUrl}}/webhooks/LreFUt-Z3sSq0JufY9uWB HTTP/1.1
|
||||
Authorization: Basic {{localCredentials}}
|
||||
|
||||
|
||||
@ -92,4 +92,24 @@ Content-Type: application/json
|
||||
{
|
||||
"token": "eTxx88nfSla87gZuJcW5mS:APA91bHGxVgSqqRtxwFHD1q9em5Oa6xSP4gO_OZRrqOoP1wjf_7UMfXKsc4uws6rWkqn73jYCc1owyATB1v61mqak4ntpqtmRkNtTey7NQXa0Wz3uQZBWY-Ecbn2rWG2VJRihOzXRId-"
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
###
|
||||
GET {{baseUrl}}/api/3rdparty/v1/webhooks HTTP/1.1
|
||||
Authorization: Basic {{credentials}}
|
||||
|
||||
###
|
||||
POST {{baseUrl}}/api/3rdparty/v1/webhooks HTTP/1.1
|
||||
Authorization: Basic {{credentials}}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"id": "MYofX8bTd5Bov0wWFZLRP",
|
||||
"url": "https://webhook.site/280a6655-eb68-40b9-b857-af5be37c5303",
|
||||
"event": "sms:received"
|
||||
}
|
||||
|
||||
###
|
||||
DELETE {{baseUrl}}/api/3rdparty/v1/webhooks/MYofX8bTd5Bov0wWFZLRP HTTP/1.1
|
||||
Authorization: Basic {{credentials}}
|
||||
|
||||
|
||||
@ -12,9 +12,11 @@ import (
|
||||
appconfig "github.com/capcom6/sms-gateway/internal/config"
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/handlers"
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/modules/auth"
|
||||
appdb "github.com/capcom6/sms-gateway/internal/sms-gateway/modules/db"
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/modules/health"
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/modules/messages"
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/modules/push"
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/modules/webhooks"
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/repositories"
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/services"
|
||||
"go.uber.org/fx"
|
||||
@ -27,6 +29,7 @@ var Module = fx.Module(
|
||||
"server",
|
||||
logger.Module,
|
||||
appconfig.Module,
|
||||
appdb.Module,
|
||||
http.Module,
|
||||
validator.Module,
|
||||
handlers.Module,
|
||||
@ -37,6 +40,7 @@ var Module = fx.Module(
|
||||
db.Module,
|
||||
messages.Module,
|
||||
health.Module,
|
||||
webhooks.Module,
|
||||
)
|
||||
|
||||
func Run() {
|
||||
|
||||
@ -5,9 +5,11 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/android-sms-gateway/client-go/smsgateway"
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/handlers/base"
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/models"
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/modules/auth"
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/modules/messages"
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/modules/webhooks"
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/repositories"
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/services"
|
||||
"github.com/capcom6/sms-gateway/pkg/types"
|
||||
@ -25,7 +27,8 @@ const (
|
||||
type ThirdPartyHandlerParams struct {
|
||||
fx.In
|
||||
|
||||
HealthHandler *healthHandler
|
||||
HealthHandler *healthHandler
|
||||
WebhooksHandler *webhooks.Handler
|
||||
|
||||
AuthSvc *auth.Service
|
||||
MessagesSvc *messages.Service
|
||||
@ -36,9 +39,10 @@ type ThirdPartyHandlerParams struct {
|
||||
}
|
||||
|
||||
type thirdPartyHandler struct {
|
||||
Handler
|
||||
base.Handler
|
||||
|
||||
healthHandler *healthHandler
|
||||
healthHandler *healthHandler
|
||||
webhooksHandler *webhooks.Handler
|
||||
|
||||
authSvc *auth.Service
|
||||
messagesSvc *messages.Service
|
||||
@ -166,8 +170,16 @@ func (h *thirdPartyHandler) getMessage(user models.User, c *fiber.Ctx) error {
|
||||
return c.JSON(state)
|
||||
}
|
||||
|
||||
func (h *thirdPartyHandler) authorize(handler func(models.User, *fiber.Ctx) error) fiber.Handler {
|
||||
return func(c *fiber.Ctx) error {
|
||||
func (h *thirdPartyHandler) Register(router fiber.Router) {
|
||||
router = router.Group("/3rdparty/v1")
|
||||
|
||||
h.healthHandler.Register(router)
|
||||
|
||||
router.Use(basicauth.New(basicauth.Config{
|
||||
Authorizer: func(username string, password string) bool {
|
||||
return len(username) > 0 && len(password) > 0
|
||||
},
|
||||
}), func(c *fiber.Ctx) error {
|
||||
username := c.Locals("username").(string)
|
||||
password := c.Locals("password").(string)
|
||||
|
||||
@ -179,33 +191,24 @@ func (h *thirdPartyHandler) authorize(handler func(models.User, *fiber.Ctx) erro
|
||||
|
||||
c.Locals("user", user)
|
||||
|
||||
return handler(user, c)
|
||||
}
|
||||
}
|
||||
return c.Next()
|
||||
})
|
||||
|
||||
func (h *thirdPartyHandler) Register(router fiber.Router) {
|
||||
router = router.Group("/3rdparty/v1")
|
||||
router.Get("/device", auth.WithUser(h.getDevice))
|
||||
|
||||
h.healthHandler.Register(router)
|
||||
router.Post("/message", auth.WithUser(h.postMessage))
|
||||
router.Get("/message/:id", auth.WithUser(h.getMessage)).Name(route3rdPartyGetMessage)
|
||||
|
||||
router.Use(basicauth.New(basicauth.Config{
|
||||
Authorizer: func(username string, password string) bool {
|
||||
return len(username) > 0 && len(password) > 0
|
||||
},
|
||||
}))
|
||||
|
||||
router.Get("/device", h.authorize(h.getDevice))
|
||||
|
||||
router.Post("/message", h.authorize(h.postMessage))
|
||||
router.Get("/message/:id", h.authorize(h.getMessage)).Name(route3rdPartyGetMessage)
|
||||
h.webhooksHandler.Register(router.Group("/webhooks"))
|
||||
}
|
||||
|
||||
func newThirdPartyHandler(params ThirdPartyHandlerParams) *thirdPartyHandler {
|
||||
return &thirdPartyHandler{
|
||||
Handler: Handler{Logger: params.Logger.Named("ThirdPartyHandler"), Validator: params.Validator},
|
||||
healthHandler: params.HealthHandler,
|
||||
authSvc: params.AuthSvc,
|
||||
messagesSvc: params.MessagesSvc,
|
||||
devicesSvc: params.DevicesSvc,
|
||||
Handler: base.Handler{Logger: params.Logger.Named("ThirdPartyHandler"), Validator: params.Validator},
|
||||
healthHandler: params.HealthHandler,
|
||||
webhooksHandler: params.WebhooksHandler,
|
||||
authSvc: params.AuthSvc,
|
||||
messagesSvc: params.MessagesSvc,
|
||||
devicesSvc: params.DevicesSvc,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package handlers
|
||||
package base
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@ -22,7 +22,7 @@ func (h *Handler) BodyParserValidator(c *fiber.Ctx, out any) error {
|
||||
return fmt.Errorf("can't parse body: %w", err)
|
||||
}
|
||||
|
||||
return h.validateStruct(out)
|
||||
return h.ValidateStruct(out)
|
||||
}
|
||||
|
||||
func (h *Handler) QueryParserValidator(c *fiber.Ctx, out any) error {
|
||||
@ -30,7 +30,7 @@ func (h *Handler) QueryParserValidator(c *fiber.Ctx, out any) error {
|
||||
return fmt.Errorf("can't parse query: %w", err)
|
||||
}
|
||||
|
||||
return h.validateStruct(out)
|
||||
return h.ValidateStruct(out)
|
||||
}
|
||||
|
||||
func (h *Handler) ParamsParserValidator(c *fiber.Ctx, out any) error {
|
||||
@ -38,10 +38,10 @@ func (h *Handler) ParamsParserValidator(c *fiber.Ctx, out any) error {
|
||||
return fmt.Errorf("can't parse params: %w", err)
|
||||
}
|
||||
|
||||
return h.validateStruct(out)
|
||||
return h.ValidateStruct(out)
|
||||
}
|
||||
|
||||
func (h *Handler) validateStruct(out any) error {
|
||||
func (h *Handler) ValidateStruct(out any) error {
|
||||
if h.Validator != nil {
|
||||
if err := h.Validator.Struct(out); err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||
@ -1,4 +1,4 @@
|
||||
package handlers
|
||||
package base
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@ -197,7 +197,7 @@ func TestHandler_validateStruct(t *testing.T) {
|
||||
Logger: tt.fields.Logger,
|
||||
Validator: tt.fields.Validator,
|
||||
}
|
||||
if err := h.validateStruct(tt.args.out); (err != nil) != tt.wantErr {
|
||||
if err := h.ValidateStruct(tt.args.out); (err != nil) != tt.wantErr {
|
||||
t.Errorf("Handler.validateStruct() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
@ -2,6 +2,7 @@ package handlers
|
||||
|
||||
import (
|
||||
"github.com/android-sms-gateway/client-go/smsgateway"
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/handlers/base"
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/modules/health"
|
||||
"github.com/capcom6/sms-gateway/internal/version"
|
||||
"github.com/capcom6/sms-gateway/pkg/maps"
|
||||
@ -19,7 +20,7 @@ type healthHanlderParams struct {
|
||||
}
|
||||
|
||||
type healthHandler struct {
|
||||
Handler
|
||||
base.Handler
|
||||
|
||||
healthSvc *health.Service
|
||||
|
||||
@ -72,7 +73,7 @@ func (h *healthHandler) Register(router fiber.Router) {
|
||||
|
||||
func newHealthHandler(params healthHanlderParams) *healthHandler {
|
||||
return &healthHandler{
|
||||
Handler: Handler{Logger: params.Logger.Named("HealthHandler"), Validator: nil},
|
||||
Handler: base.Handler{Logger: params.Logger.Named("HealthHandler"), Validator: nil},
|
||||
healthSvc: params.HealthSvc,
|
||||
logger: params.Logger,
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/android-sms-gateway/client-go/smsgateway"
|
||||
"github.com/capcom6/go-infra-fx/http/apikey"
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/handlers/base"
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/models"
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/modules/auth"
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/modules/messages"
|
||||
@ -20,7 +21,7 @@ import (
|
||||
)
|
||||
|
||||
type mobileHandler struct {
|
||||
Handler
|
||||
base.Handler
|
||||
|
||||
authSvc *auth.Service
|
||||
messagesSvc *messages.Service
|
||||
@ -142,7 +143,7 @@ func (h *mobileHandler) patchMessage(device models.Device, c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
for _, v := range req {
|
||||
if err := h.validateStruct(v); err != nil {
|
||||
if err := h.ValidateStruct(v); err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||
}
|
||||
|
||||
@ -205,7 +206,7 @@ func newMobileHandler(params MobileHandlerParams) *mobileHandler {
|
||||
idGen, _ := nanoid.Standard(21)
|
||||
|
||||
return &mobileHandler{
|
||||
Handler: Handler{Logger: params.Logger, Validator: params.Validator},
|
||||
Handler: base.Handler{Logger: params.Logger, Validator: params.Validator},
|
||||
authSvc: params.AuthSvc,
|
||||
messagesSvc: params.MessagesSvc,
|
||||
idGen: idGen,
|
||||
|
||||
@ -4,6 +4,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/android-sms-gateway/client-go/smsgateway"
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/handlers/base"
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/modules/push"
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
@ -13,7 +14,7 @@ import (
|
||||
)
|
||||
|
||||
type upstreamHandler struct {
|
||||
Handler
|
||||
base.Handler
|
||||
|
||||
config Config
|
||||
pushSvc *push.Service
|
||||
@ -31,7 +32,7 @@ type upstreamHandlerParams struct {
|
||||
|
||||
func newUpstreamHandler(params upstreamHandlerParams) *upstreamHandler {
|
||||
return &upstreamHandler{
|
||||
Handler: Handler{Logger: params.Logger, Validator: params.Validator},
|
||||
Handler: base.Handler{Logger: params.Logger, Validator: params.Validator},
|
||||
config: params.Config,
|
||||
pushSvc: params.PushSvc,
|
||||
}
|
||||
@ -62,7 +63,7 @@ func (h *upstreamHandler) postPush(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
for _, v := range req {
|
||||
if err := h.validateStruct(v); err != nil {
|
||||
if err := h.ValidateStruct(v); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,21 @@
|
||||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
CREATE TABLE `webhooks` (
|
||||
`id` BIGINT UNSIGNED AUTO_INCREMENT,
|
||||
`ext_id` varchar(36) NOT NULL,
|
||||
`user_id` varchar(32) NOT NULL,
|
||||
`url` varchar(256) NOT NULL,
|
||||
`event` varchar(32) NOT NULL,
|
||||
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updated_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
`deleted_at` datetime(3) NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE INDEX `unq_webhooks_user_extid` (`user_id`, `ext_id`),
|
||||
CONSTRAINT `fk_webhooks_user` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE
|
||||
);
|
||||
-- +goose StatementEnd
|
||||
---
|
||||
-- +goose Down
|
||||
-- +goose StatementBegin
|
||||
DROP TABLE `webhooks`;
|
||||
-- +goose StatementEnd
|
||||
@ -15,9 +15,9 @@ const (
|
||||
)
|
||||
|
||||
type TimedModel struct {
|
||||
CreatedAt time.Time `gorm:"not null;autocreatetime:false;default:CURRENT_TIMESTAMP(3)"`
|
||||
UpdatedAt time.Time `gorm:"not null;autoupdatetime:false;default:CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"`
|
||||
DeletedAt *time.Time
|
||||
CreatedAt time.Time `gorm:"->;not null;autocreatetime:false;default:CURRENT_TIMESTAMP(3)"`
|
||||
UpdatedAt time.Time `gorm:"->;not null;autoupdatetime:false;default:CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"`
|
||||
DeletedAt *time.Time `gorm:"<-:update"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
|
||||
12
internal/sms-gateway/modules/auth/decorators.go
Normal file
12
internal/sms-gateway/modules/auth/decorators.go
Normal file
@ -0,0 +1,12 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/models"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func WithUser(handler func(models.User, *fiber.Ctx) error) fiber.Handler {
|
||||
return func(c *fiber.Ctx) error {
|
||||
return handler(c.Locals("user").(models.User), c)
|
||||
}
|
||||
}
|
||||
15
internal/sms-gateway/modules/db/module.go
Normal file
15
internal/sms-gateway/modules/db/module.go
Normal file
@ -0,0 +1,15 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"github.com/jaevor/go-nanoid"
|
||||
"go.uber.org/fx"
|
||||
)
|
||||
|
||||
type IDGen func() string
|
||||
|
||||
var Module = fx.Module(
|
||||
"db",
|
||||
fx.Provide(func() (IDGen, error) {
|
||||
return nanoid.Standard(21)
|
||||
}),
|
||||
)
|
||||
@ -10,10 +10,10 @@ import (
|
||||
"github.com/android-sms-gateway/client-go/smsgateway"
|
||||
"github.com/capcom6/go-helpers/slices"
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/models"
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/modules/db"
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/modules/push"
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/repositories"
|
||||
"github.com/capcom6/sms-gateway/pkg/types"
|
||||
"github.com/jaevor/go-nanoid"
|
||||
"github.com/nyaruka/phonenumbers"
|
||||
"go.uber.org/fx"
|
||||
"go.uber.org/zap"
|
||||
@ -37,6 +37,8 @@ type EnqueueOptions struct {
|
||||
type ServiceParams struct {
|
||||
fx.In
|
||||
|
||||
IDGen db.IDGen
|
||||
|
||||
Messages *repositories.MessagesRepository
|
||||
HashingTask *HashingTask
|
||||
|
||||
@ -55,8 +57,6 @@ type Service struct {
|
||||
}
|
||||
|
||||
func NewService(params ServiceParams) *Service {
|
||||
idgen, _ := nanoid.Standard(21)
|
||||
|
||||
return &Service{
|
||||
Messages: params.Messages,
|
||||
HashingTask: params.HashingTask,
|
||||
@ -64,7 +64,7 @@ func NewService(params ServiceParams) *Service {
|
||||
PushSvc: params.PushSvc,
|
||||
Logger: params.Logger.Named("Service"),
|
||||
|
||||
idgen: idgen,
|
||||
idgen: params.IDGen,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
9
internal/sms-gateway/modules/webhooks/converters.go
Normal file
9
internal/sms-gateway/modules/webhooks/converters.go
Normal file
@ -0,0 +1,9 @@
|
||||
package webhooks
|
||||
|
||||
func webhookToDTO(model *Webhook) WebhookDTO {
|
||||
return WebhookDTO{
|
||||
ID: model.ExtID,
|
||||
URL: model.URL,
|
||||
Event: model.Event,
|
||||
}
|
||||
}
|
||||
7
internal/sms-gateway/modules/webhooks/dto.go
Normal file
7
internal/sms-gateway/modules/webhooks/dto.go
Normal file
@ -0,0 +1,7 @@
|
||||
package webhooks
|
||||
|
||||
type WebhookDTO struct {
|
||||
ID string `json:"id" validate:"max=36" example:"123e4567-e89b-12d3-a456-426614174000"`
|
||||
URL string `json:"url" validate:"required,http_url" example:"https://example.com/webhook"`
|
||||
Event Event `json:"event" validate:"required" example:"sms:received"`
|
||||
}
|
||||
80
internal/sms-gateway/modules/webhooks/handler.go
Normal file
80
internal/sms-gateway/modules/webhooks/handler.go
Normal file
@ -0,0 +1,80 @@
|
||||
package webhooks
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/handlers/base"
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/models"
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/modules/auth"
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"go.uber.org/fx"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type handlerParams struct {
|
||||
fx.In
|
||||
|
||||
WebhooksSvc *Service
|
||||
|
||||
Validator *validator.Validate
|
||||
Logger *zap.Logger
|
||||
}
|
||||
|
||||
type Handler struct {
|
||||
base.Handler
|
||||
|
||||
webhooksSvc *Service
|
||||
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
func (h *Handler) get(user models.User, c *fiber.Ctx) error {
|
||||
items, err := h.webhooksSvc.Select(user.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't select webhooks: %w", err)
|
||||
}
|
||||
|
||||
return c.JSON(items)
|
||||
}
|
||||
|
||||
func (h *Handler) post(user models.User, c *fiber.Ctx) error {
|
||||
dto := WebhookDTO{}
|
||||
|
||||
if err := h.BodyParserValidator(c, &dto); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := h.webhooksSvc.Replace(user.ID, dto); err != nil {
|
||||
return fmt.Errorf("can't write webhook: %w", err)
|
||||
}
|
||||
|
||||
return c.SendStatus(fiber.StatusCreated)
|
||||
}
|
||||
|
||||
func (h *Handler) delete(user models.User, c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
|
||||
if err := h.webhooksSvc.Delete(user.ID, WithExtID(id)); err != nil {
|
||||
return fmt.Errorf("can't delete webhook: %w", err)
|
||||
}
|
||||
|
||||
return c.SendStatus(fiber.StatusNoContent)
|
||||
}
|
||||
|
||||
func (h *Handler) Register(router fiber.Router) {
|
||||
router.Get("/", auth.WithUser(h.get))
|
||||
router.Post("/", auth.WithUser(h.post))
|
||||
router.Delete("/:id", auth.WithUser(h.delete))
|
||||
}
|
||||
|
||||
func NewHandler(params handlerParams) *Handler {
|
||||
return &Handler{
|
||||
Handler: base.Handler{
|
||||
Logger: params.Logger.Named("webhooks"),
|
||||
Validator: params.Validator,
|
||||
},
|
||||
webhooksSvc: params.WebhooksSvc,
|
||||
logger: params.Logger,
|
||||
}
|
||||
}
|
||||
23
internal/sms-gateway/modules/webhooks/models.go
Normal file
23
internal/sms-gateway/modules/webhooks/models.go
Normal file
@ -0,0 +1,23 @@
|
||||
package webhooks
|
||||
|
||||
import (
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/models"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Webhook struct {
|
||||
ID uint64 `json:"-" gorm:"->;primaryKey;type:BIGINT UNSIGNED;autoIncrement"`
|
||||
ExtID string `json:"id" gorm:"not null;type:varchar(36);uniqueIndex:unq_webhooks_user_extid,priority:2"`
|
||||
UserID string `json:"-" gorm:"<-:create;not null;type:varchar(32);uniqueIndex:unq_webhooks_user_extid,priority:1"`
|
||||
|
||||
URL string `json:"url" validate:"required,http_url" gorm:"not null;type:varchar(256)"`
|
||||
Event Event `json:"event" gorm:"not null;type:varchar(32)"`
|
||||
|
||||
User models.User `gorm:"foreignKey:UserID;constraint:OnDelete:CASCADE"`
|
||||
|
||||
models.TimedModel
|
||||
}
|
||||
|
||||
func Migrate(db *gorm.DB) error {
|
||||
return db.AutoMigrate(&Webhook{})
|
||||
}
|
||||
23
internal/sms-gateway/modules/webhooks/module.go
Normal file
23
internal/sms-gateway/modules/webhooks/module.go
Normal file
@ -0,0 +1,23 @@
|
||||
package webhooks
|
||||
|
||||
import (
|
||||
"github.com/capcom6/go-infra-fx/db"
|
||||
"go.uber.org/fx"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var Module = fx.Module(
|
||||
"webhooks",
|
||||
fx.Decorate(func(log *zap.Logger) *zap.Logger {
|
||||
return log.Named("webhooks")
|
||||
}),
|
||||
fx.Provide(NewRepository, fx.Private),
|
||||
fx.Provide(
|
||||
NewService,
|
||||
NewHandler,
|
||||
),
|
||||
)
|
||||
|
||||
func init() {
|
||||
db.RegisterMigration(Migrate)
|
||||
}
|
||||
35
internal/sms-gateway/modules/webhooks/repository.go
Normal file
35
internal/sms-gateway/modules/webhooks/repository.go
Normal file
@ -0,0 +1,35 @@
|
||||
package webhooks
|
||||
|
||||
import (
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
type Repository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
func (r *Repository) Select(filters ...SelectFilter) ([]*Webhook, error) {
|
||||
webhooks := []*Webhook{}
|
||||
if err := newFilter(filters...).apply(r.db).Find(&webhooks).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return webhooks, nil
|
||||
}
|
||||
|
||||
func (r *Repository) Replace(webhook *Webhook) error {
|
||||
return r.db.
|
||||
Clauses(clause.OnConflict{UpdateAll: true}).
|
||||
Save(webhook).
|
||||
Error
|
||||
}
|
||||
|
||||
func (r *Repository) Delete(filters ...SelectFilter) error {
|
||||
return newFilter(filters...).apply(r.db).Delete(&Webhook{}).Error
|
||||
}
|
||||
|
||||
func NewRepository(db *gorm.DB) *Repository {
|
||||
return &Repository{
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
42
internal/sms-gateway/modules/webhooks/repository_filter.go
Normal file
42
internal/sms-gateway/modules/webhooks/repository_filter.go
Normal file
@ -0,0 +1,42 @@
|
||||
package webhooks
|
||||
|
||||
import "gorm.io/gorm"
|
||||
|
||||
type SelectFilter func(*selectFilter)
|
||||
|
||||
func WithExtID(extID string) SelectFilter {
|
||||
return func(f *selectFilter) {
|
||||
f.extID = &extID
|
||||
}
|
||||
}
|
||||
|
||||
func WithUserID(userID string) SelectFilter {
|
||||
return func(f *selectFilter) {
|
||||
f.userID = userID
|
||||
}
|
||||
}
|
||||
|
||||
type selectFilter struct {
|
||||
userID string
|
||||
extID *string
|
||||
}
|
||||
|
||||
func newFilter(filters ...SelectFilter) *selectFilter {
|
||||
f := &selectFilter{}
|
||||
f.merge(filters...)
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *selectFilter) merge(filters ...SelectFilter) {
|
||||
for _, filter := range filters {
|
||||
filter(f)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *selectFilter) apply(query *gorm.DB) *gorm.DB {
|
||||
query = query.Where("user_id = ?", f.userID)
|
||||
if f.extID != nil {
|
||||
query = query.Where("ext_id = ?", *f.extID)
|
||||
}
|
||||
return query
|
||||
}
|
||||
52
internal/sms-gateway/modules/webhooks/service.go
Normal file
52
internal/sms-gateway/modules/webhooks/service.go
Normal file
@ -0,0 +1,52 @@
|
||||
package webhooks
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/capcom6/go-helpers/slices"
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/modules/db"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
idgen db.IDGen
|
||||
|
||||
webhooks *Repository
|
||||
}
|
||||
|
||||
func NewService(idgen db.IDGen, webhooks *Repository) *Service {
|
||||
return &Service{
|
||||
idgen: idgen,
|
||||
webhooks: webhooks,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) Select(userID string, filters ...SelectFilter) ([]WebhookDTO, error) {
|
||||
filters = append(filters, WithUserID(userID))
|
||||
|
||||
items, err := s.webhooks.Select(filters...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't select webhooks: %w", err)
|
||||
}
|
||||
|
||||
return slices.Map(items, webhookToDTO), nil
|
||||
}
|
||||
|
||||
func (s *Service) Replace(userID string, webhook WebhookDTO) error {
|
||||
if webhook.ID == "" {
|
||||
webhook.ID = s.idgen()
|
||||
}
|
||||
|
||||
model := Webhook{
|
||||
ExtID: webhook.ID,
|
||||
UserID: userID,
|
||||
URL: webhook.URL,
|
||||
Event: webhook.Event,
|
||||
}
|
||||
|
||||
return s.webhooks.Replace(&model)
|
||||
}
|
||||
|
||||
func (s *Service) Delete(userID string, filters ...SelectFilter) error {
|
||||
filters = append(filters, WithUserID(userID))
|
||||
return s.webhooks.Delete(filters...)
|
||||
}
|
||||
7
internal/sms-gateway/modules/webhooks/types.go
Normal file
7
internal/sms-gateway/modules/webhooks/types.go
Normal file
@ -0,0 +1,7 @@
|
||||
package webhooks
|
||||
|
||||
type Event string
|
||||
|
||||
const (
|
||||
EventSmsReceived Event = "sms:received"
|
||||
)
|
||||
Loading…
x
Reference in New Issue
Block a user