[webhooks] add device endpoint

This commit is contained in:
Aleksandr Soloshenko 2024-06-09 20:41:02 +07:00
parent c0ce84e89f
commit 4d4f1e0bcd
19 changed files with 648 additions and 164 deletions

43
api/mobile.http Normal file
View File

@ -0,0 +1,43 @@
@baseUrl={{$dotenv CLOUD__URL}}/api/mobile/v1
@mobileToken={{$dotenv MOBILE__TOKEN}}
@phone={{$dotenv PHONE}}
###
POST {{baseUrl}}/device HTTP/1.1
Authorization: Bearer 123456789
Content-Type: application/json
{
"name": "Android Phone",
"pushToken": "eTxx88nfSla87gZuJcW5mS:APA91bHGxVgSqqRtxwFHD1q9em5Oa6xSP4gO_OZRrqOoP1wjf_7UMfXKsc4uws6rWkqn73jYCc1owyATB1v61mqak4ntpqtmRkNtTey7NQXa0Wz3uQZBWY-Ecbn2rWG2VJRihOzXRId-"
}
###
GET {{baseUrl}}/message HTTP/1.1
Authorization: Bearer {{mobileToken}}
###
PATCH {{baseUrl}}/message HTTP/1.1
Authorization: Bearer {{mobileToken}}
Content-Type: application/json
[
{
"id": "2dcIAhcLg81cez7GE_Pdp",
"state": "Failed",
"recipients": [
{
"phoneNumber": "{{phone}}",
"state": "Failed"
}
],
"states": {
"Processed": "2024-05-13T16:49:17.357+07:00",
"Failed": "2024-05-13T16:49:17.357+07:00"
}
}
]
###
GET {{baseUrl}}/webhooks HTTP/1.1
Authorization: Bearer {{mobileToken}}

View File

@ -9,16 +9,6 @@ GET {{baseUrl}}/health HTTP/1.1
###
GET {{baseUrl}}/api/3rdparty/v1/health HTTP/1.1
###
POST {{baseUrl}}/api/mobile/v1/device HTTP/1.1
Authorization: Bearer 123456789
Content-Type: application/json
{
"name": "Android Phone",
"pushToken": "eTxx88nfSla87gZuJcW5mS:APA91bHGxVgSqqRtxwFHD1q9em5Oa6xSP4gO_OZRrqOoP1wjf_7UMfXKsc4uws6rWkqn73jYCc1owyATB1v61mqak4ntpqtmRkNtTey7NQXa0Wz3uQZBWY-Ecbn2rWG2VJRihOzXRId-"
}
###
POST {{baseUrl}}/api/3rdparty/v1/message?skipPhoneValidation=false HTTP/1.1
Content-Type: application/json
@ -58,32 +48,6 @@ Authorization: Basic {{credentials}}
GET {{baseUrl}}/api/3rdparty/v1/device HTTP/1.1
Authorization: Basic {{credentials}}
###
GET {{baseUrl}}/api/mobile/v1/message HTTP/1.1
Authorization: Bearer {{mobileToken}}
###
PATCH {{baseUrl}}/api/mobile/v1/message HTTP/1.1
Authorization: Bearer {{mobileToken}}
Content-Type: application/json
[
{
"id": "2dcIAhcLg81cez7GE_Pdp",
"state": "Failed",
"recipients": [
{
"phoneNumber": "{{phone}}",
"state": "Failed"
}
],
"states": {
"Processed": "2024-05-13T16:49:17.357+07:00",
"Failed": "2024-05-13T16:49:17.357+07:00"
}
}
]
###
POST {{baseUrl}}/api/upstream/v1/push HTTP/1.1
Content-Type: application/json

View File

@ -208,6 +208,150 @@
}
}
},
"/api/v1/3rdparty/webhooks": {
"get": {
"security": [
{
"ApiAuth": []
}
],
"description": "Returns list of registered webhooks",
"produces": [
"application/json"
],
"tags": [
"User",
"Webhooks"
],
"summary": "List webhooks",
"responses": {
"200": {
"description": "Webhook list",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/github_com_capcom6_sms-gateway_pkg_smsgateway.WebhookDTO"
}
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/smsgateway.ErrorResponse"
}
},
"500": {
"description": "Internal server error",
"schema": {
"$ref": "#/definitions/smsgateway.ErrorResponse"
}
}
}
},
"post": {
"security": [
{
"ApiAuth": []
}
],
"description": "Registers webhook. If webhook with same ID already exists, it will be replaced",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"User",
"Webhooks"
],
"summary": "Register webhook",
"parameters": [
{
"description": "Webhook",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/github_com_capcom6_sms-gateway_pkg_smsgateway.WebhookDTO"
}
}
],
"responses": {
"201": {
"description": "Created",
"schema": {
"$ref": "#/definitions/github_com_capcom6_sms-gateway_pkg_smsgateway.WebhookDTO"
}
},
"400": {
"description": "Invalid request",
"schema": {
"$ref": "#/definitions/smsgateway.ErrorResponse"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/smsgateway.ErrorResponse"
}
},
"500": {
"description": "Internal server error",
"schema": {
"$ref": "#/definitions/smsgateway.ErrorResponse"
}
}
}
}
},
"/api/v1/3rdparty/webhooks/{id}": {
"delete": {
"security": [
{
"ApiAuth": []
}
],
"description": "Deletes webhook",
"produces": [
"application/json"
],
"tags": [
"User",
"Webhooks"
],
"summary": "Delete webhook",
"parameters": [
{
"type": "string",
"description": "Webhook ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"204": {
"description": "Webhook deleted",
"schema": {
"type": "object"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/smsgateway.ErrorResponse"
}
},
"500": {
"description": "Internal server error",
"schema": {
"$ref": "#/definitions/smsgateway.ErrorResponse"
}
}
}
}
},
"/health": {
"get": {
"description": "Checks if service is healthy",
@ -429,6 +573,47 @@
}
}
},
"/mobile/v1/webhooks": {
"get": {
"security": [
{
"MobileToken": []
}
],
"description": "Returns list of registered webhooks for device",
"produces": [
"application/json"
],
"tags": [
"Device",
"Webhooks"
],
"summary": "List webhooks",
"responses": {
"200": {
"description": "Webhook list",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/github_com_capcom6_sms-gateway_pkg_smsgateway.WebhookDTO"
}
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/smsgateway.ErrorResponse"
}
},
"500": {
"description": "Internal server error",
"schema": {
"$ref": "#/definitions/smsgateway.ErrorResponse"
}
}
}
}
},
"/upstream/v1/push": {
"post": {
"description": "Enqueues notifications for sending to devices",
@ -483,6 +668,41 @@
}
},
"definitions": {
"github_com_capcom6_sms-gateway_pkg_smsgateway.WebhookDTO": {
"type": "object",
"required": [
"event",
"url"
],
"properties": {
"event": {
"allOf": [
{
"$ref": "#/definitions/github_com_capcom6_sms-gateway_pkg_smsgateway.WebhookEvent"
}
],
"example": "sms:received"
},
"id": {
"type": "string",
"maxLength": 36,
"example": "123e4567-e89b-12d3-a456-426614174000"
},
"url": {
"type": "string",
"example": "https://example.com/webhook"
}
}
},
"github_com_capcom6_sms-gateway_pkg_smsgateway.WebhookEvent": {
"type": "string",
"enum": [
"sms:received"
],
"x-enum-varnames": [
"WebhookEventSmsReceived"
]
},
"smsgateway.Device": {
"type": "object",
"properties": {

View File

@ -1,5 +1,28 @@
basePath: /api
definitions:
github_com_capcom6_sms-gateway_pkg_smsgateway.WebhookDTO:
properties:
event:
allOf:
- $ref: '#/definitions/github_com_capcom6_sms-gateway_pkg_smsgateway.WebhookEvent'
example: sms:received
id:
example: 123e4567-e89b-12d3-a456-426614174000
maxLength: 36
type: string
url:
example: https://example.com/webhook
type: string
required:
- event
- url
type: object
github_com_capcom6_sms-gateway_pkg_smsgateway.WebhookEvent:
enum:
- sms:received
type: string
x-enum-varnames:
- WebhookEventSmsReceived
smsgateway.Device:
properties:
createdAt:
@ -402,6 +425,99 @@ paths:
summary: Health check
tags:
- System
/api/v1/3rdparty/webhooks:
get:
description: Returns list of registered webhooks
produces:
- application/json
responses:
"200":
description: Webhook list
schema:
items:
$ref: '#/definitions/github_com_capcom6_sms-gateway_pkg_smsgateway.WebhookDTO'
type: array
"401":
description: Unauthorized
schema:
$ref: '#/definitions/smsgateway.ErrorResponse'
"500":
description: Internal server error
schema:
$ref: '#/definitions/smsgateway.ErrorResponse'
security:
- ApiAuth: []
summary: List webhooks
tags:
- User
- Webhooks
post:
consumes:
- application/json
description: Registers webhook. If webhook with same ID already exists, it will
be replaced
parameters:
- description: Webhook
in: body
name: request
required: true
schema:
$ref: '#/definitions/github_com_capcom6_sms-gateway_pkg_smsgateway.WebhookDTO'
produces:
- application/json
responses:
"201":
description: Created
schema:
$ref: '#/definitions/github_com_capcom6_sms-gateway_pkg_smsgateway.WebhookDTO'
"400":
description: Invalid request
schema:
$ref: '#/definitions/smsgateway.ErrorResponse'
"401":
description: Unauthorized
schema:
$ref: '#/definitions/smsgateway.ErrorResponse'
"500":
description: Internal server error
schema:
$ref: '#/definitions/smsgateway.ErrorResponse'
security:
- ApiAuth: []
summary: Register webhook
tags:
- User
- Webhooks
/api/v1/3rdparty/webhooks/{id}:
delete:
description: Deletes webhook
parameters:
- description: Webhook ID
in: path
name: id
required: true
type: string
produces:
- application/json
responses:
"204":
description: Webhook deleted
schema:
type: object
"401":
description: Unauthorized
schema:
$ref: '#/definitions/smsgateway.ErrorResponse'
"500":
description: Internal server error
schema:
$ref: '#/definitions/smsgateway.ErrorResponse'
security:
- ApiAuth: []
summary: Delete webhook
tags:
- User
- Webhooks
/health:
get:
description: Checks if service is healthy
@ -544,6 +660,32 @@ paths:
tags:
- Device
- Messages
/mobile/v1/webhooks:
get:
description: Returns list of registered webhooks for device
produces:
- application/json
responses:
"200":
description: Webhook list
schema:
items:
$ref: '#/definitions/github_com_capcom6_sms-gateway_pkg_smsgateway.WebhookDTO'
type: array
"401":
description: Unauthorized
schema:
$ref: '#/definitions/smsgateway.ErrorResponse'
"500":
description: Internal server error
schema:
$ref: '#/definitions/smsgateway.ErrorResponse'
security:
- MobileToken: []
summary: List webhooks
tags:
- Device
- Webhooks
/upstream/v1/push:
post:
consumes:

View File

@ -6,10 +6,10 @@ 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/handlers/webhooks"
"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"
@ -28,7 +28,7 @@ type ThirdPartyHandlerParams struct {
fx.In
HealthHandler *healthHandler
WebhooksHandler *webhooks.Handler
WebhooksHandler *webhooks.ThirdPartyController
AuthSvc *auth.Service
MessagesSvc *messages.Service
@ -42,7 +42,7 @@ type thirdPartyHandler struct {
base.Handler
healthHandler *healthHandler
webhooksHandler *webhooks.Handler
webhooksHandler *webhooks.ThirdPartyController
authSvc *auth.Service
messagesSvc *messages.Service

View File

@ -8,6 +8,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/handlers/webhooks"
"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"
@ -26,6 +27,8 @@ type mobileHandler struct {
authSvc *auth.Service
messagesSvc *messages.Service
webhooksCtrl *webhooks.MobileController
idGen func() string
}
@ -156,20 +159,6 @@ func (h *mobileHandler) patchMessage(device models.Device, c *fiber.Ctx) error {
return c.SendStatus(fiber.StatusNoContent)
}
func (h *mobileHandler) authorize(handler func(models.Device, *fiber.Ctx) error) fiber.Handler {
return func(c *fiber.Ctx) error {
token := c.Locals("token").(string)
device, err := h.authSvc.AuthorizeDevice(token)
if err != nil {
h.Logger.Error("Can't authorize device", zap.Error(err))
return fiber.ErrUnauthorized
}
return handler(device, c)
}
}
func (h *mobileHandler) Register(router fiber.Router) {
router = router.Group("/mobile/v1")
@ -184,15 +173,29 @@ func (h *mobileHandler) Register(router fiber.Router) {
Authorizer: func(token string) bool {
return len(token) > 0
},
}))
}), func(c *fiber.Ctx) error {
token := c.Locals("token").(string)
router.Patch("/device", h.authorize(h.patchDevice))
device, err := h.authSvc.AuthorizeDevice(token)
if err != nil {
h.Logger.Error("Can't authorize device", zap.Error(err))
return fiber.ErrUnauthorized
}
router.Get("/message", h.authorize(h.getMessage))
router.Patch("/message", h.authorize(h.patchMessage))
c.Locals("device", device)
return c.Next()
})
router.Patch("/device", auth.WithDevice(h.patchDevice))
router.Get("/message", auth.WithDevice(h.getMessage))
router.Patch("/message", auth.WithDevice(h.patchMessage))
h.webhooksCtrl.Register(router.Group("/webhooks"))
}
type MobileHandlerParams struct {
type mobileHandlerParams struct {
fx.In
Logger *zap.Logger
@ -200,15 +203,18 @@ type MobileHandlerParams struct {
AuthSvc *auth.Service
MessagesSvc *messages.Service
WebhooksCtrl *webhooks.MobileController
}
func newMobileHandler(params MobileHandlerParams) *mobileHandler {
func newMobileHandler(params mobileHandlerParams) *mobileHandler {
idGen, _ := nanoid.Standard(21)
return &mobileHandler{
Handler: base.Handler{Logger: params.Logger, Validator: params.Validator},
authSvc: params.AuthSvc,
messagesSvc: params.MessagesSvc,
idGen: idGen,
Handler: base.Handler{Logger: params.Logger, Validator: params.Validator},
authSvc: params.AuthSvc,
messagesSvc: params.MessagesSvc,
webhooksCtrl: params.WebhooksCtrl,
idGen: idGen,
}
}

View File

@ -2,6 +2,7 @@ package handlers
import (
"github.com/capcom6/go-infra-fx/http"
"github.com/capcom6/sms-gateway/internal/sms-gateway/handlers/webhooks"
"go.uber.org/fx"
"go.uber.org/zap"
)
@ -19,6 +20,8 @@ var Module = fx.Module(
),
fx.Provide(
newHealthHandler,
webhooks.NewThirdPartyController,
webhooks.NewMobileController,
fx.Private,
),
)

View File

@ -0,0 +1,116 @@
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/capcom6/sms-gateway/internal/sms-gateway/modules/webhooks"
"github.com/capcom6/sms-gateway/pkg/smsgateway"
"github.com/go-playground/validator/v10"
"github.com/gofiber/fiber/v2"
"go.uber.org/fx"
"go.uber.org/zap"
)
type thirdPartyControllerParams struct {
fx.In
WebhooksSvc *webhooks.Service
Validator *validator.Validate
Logger *zap.Logger
}
type ThirdPartyController struct {
base.Handler
webhooksSvc *webhooks.Service
}
// @Summary List webhooks
// @Description Returns list of registered webhooks
// @Security ApiAuth
// @Tags User, Webhooks
// @Produce json
// @Success 200 {object} []smsgateway.WebhookDTO "Webhook list"
// @Failure 401 {object} smsgateway.ErrorResponse "Unauthorized"
// @Failure 500 {object} smsgateway.ErrorResponse "Internal server error"
// @Router /api/v1/3rdparty/webhooks [get]
//
// List webhooks
func (h *ThirdPartyController) 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)
}
// @Summary Register webhook
// @Description Registers webhook. If webhook with same ID already exists, it will be replaced
// @Security ApiAuth
// @Tags User, Webhooks
// @Accept json
// @Produce json
// @Param request body smsgateway.WebhookDTO true "Webhook"
// @Success 201 {object} smsgateway.WebhookDTO "Created"
// @Failure 400 {object} smsgateway.ErrorResponse "Invalid request"
// @Failure 401 {object} smsgateway.ErrorResponse "Unauthorized"
// @Failure 500 {object} smsgateway.ErrorResponse "Internal server error"
// @Router /api/v1/3rdparty/webhooks [post]
//
// Register webhook
func (h *ThirdPartyController) post(user models.User, c *fiber.Ctx) error {
dto := &smsgateway.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.Status(fiber.StatusCreated).JSON(dto)
}
// @Summary Delete webhook
// @Description Deletes webhook
// @Security ApiAuth
// @Tags User, Webhooks
// @Produce json
// @Param id path string true "Webhook ID"
// @Success 204 {object} object "Webhook deleted"
// @Failure 401 {object} smsgateway.ErrorResponse "Unauthorized"
// @Failure 500 {object} smsgateway.ErrorResponse "Internal server error"
// @Router /api/v1/3rdparty/webhooks/{id} [delete]
//
// Delete webhook
func (h *ThirdPartyController) delete(user models.User, c *fiber.Ctx) error {
id := c.Params("id")
if err := h.webhooksSvc.Delete(user.ID, webhooks.WithExtID(id)); err != nil {
return fmt.Errorf("can't delete webhook: %w", err)
}
return c.SendStatus(fiber.StatusNoContent)
}
func (h *ThirdPartyController) Register(router fiber.Router) {
router.Get("/", auth.WithUser(h.get))
router.Post("/", auth.WithUser(h.post))
router.Delete("/:id", auth.WithUser(h.delete))
}
func NewThirdPartyController(params thirdPartyControllerParams) *ThirdPartyController {
return &ThirdPartyController{
Handler: base.Handler{
Logger: params.Logger.Named("controller"),
Validator: params.Validator,
},
webhooksSvc: params.WebhooksSvc,
}
}

View File

@ -0,0 +1,61 @@
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/capcom6/sms-gateway/internal/sms-gateway/modules/webhooks"
_ "github.com/capcom6/sms-gateway/pkg/smsgateway"
"github.com/gofiber/fiber/v2"
"go.uber.org/fx"
"go.uber.org/zap"
)
type mobileControllerParams struct {
fx.In
WebhooksServices *webhooks.Service
Logger *zap.Logger
}
type MobileController struct {
base.Handler
webhooksSvc *webhooks.Service
}
// @Summary List webhooks
// @Description Returns list of registered webhooks for device
// @Security MobileToken
// @Tags Device, Webhooks
// @Produce json
// @Success 200 {object} []smsgateway.WebhookDTO "Webhook list"
// @Failure 401 {object} smsgateway.ErrorResponse "Unauthorized"
// @Failure 500 {object} smsgateway.ErrorResponse "Internal server error"
// @Router /mobile/v1/webhooks [get]
//
// List webhooks
func (h *MobileController) get(device models.Device, c *fiber.Ctx) error {
items, err := h.webhooksSvc.Select(device.UserID)
if err != nil {
return fmt.Errorf("can't select webhooks: %w", err)
}
return c.JSON(items)
}
func (h *MobileController) Register(router fiber.Router) {
router.Get("/", auth.WithDevice(h.get))
}
func NewMobileController(params mobileControllerParams) *MobileController {
return &MobileController{
Handler: base.Handler{
Logger: params.Logger.Named("mobile"),
},
webhooksSvc: params.WebhooksServices,
}
}

View File

@ -10,3 +10,9 @@ func WithUser(handler func(models.User, *fiber.Ctx) error) fiber.Handler {
return handler(c.Locals("user").(models.User), c)
}
}
func WithDevice(handler func(models.Device, *fiber.Ctx) error) fiber.Handler {
return func(c *fiber.Ctx) error {
return handler(c.Locals("device").(models.Device), c)
}
}

View File

@ -1,7 +1,9 @@
package webhooks
func webhookToDTO(model *Webhook) WebhookDTO {
return WebhookDTO{
import "github.com/capcom6/sms-gateway/pkg/smsgateway"
func webhookToDTO(model *Webhook) smsgateway.WebhookDTO {
return smsgateway.WebhookDTO{
ID: model.ExtID,
URL: model.URL,
Event: model.Event,

View File

@ -1,7 +0,0 @@
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"`
}

View File

@ -1,80 +0,0 @@
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,
}
}

View File

@ -2,6 +2,7 @@ package webhooks
import (
"github.com/capcom6/sms-gateway/internal/sms-gateway/models"
"github.com/capcom6/sms-gateway/pkg/smsgateway"
"gorm.io/gorm"
)
@ -10,8 +11,8 @@ type Webhook struct {
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)"`
URL string `json:"url" validate:"required,http_url" gorm:"not null;type:varchar(256)"`
Event smsgateway.WebhookEvent `json:"event" gorm:"not null;type:varchar(32)"`
User models.User `gorm:"foreignKey:UserID;constraint:OnDelete:CASCADE"`

View File

@ -14,7 +14,6 @@ var Module = fx.Module(
fx.Provide(NewRepository, fx.Private),
fx.Provide(
NewService,
NewHandler,
),
)

View File

@ -5,6 +5,7 @@ import (
"github.com/capcom6/go-helpers/slices"
"github.com/capcom6/sms-gateway/internal/sms-gateway/modules/db"
"github.com/capcom6/sms-gateway/pkg/smsgateway"
)
type Service struct {
@ -20,7 +21,7 @@ func NewService(idgen db.IDGen, webhooks *Repository) *Service {
}
}
func (s *Service) Select(userID string, filters ...SelectFilter) ([]WebhookDTO, error) {
func (s *Service) Select(userID string, filters ...SelectFilter) ([]smsgateway.WebhookDTO, error) {
filters = append(filters, WithUserID(userID))
items, err := s.webhooks.Select(filters...)
@ -31,7 +32,7 @@ func (s *Service) Select(userID string, filters ...SelectFilter) ([]WebhookDTO,
return slices.Map(items, webhookToDTO), nil
}
func (s *Service) Replace(userID string, webhook WebhookDTO) error {
func (s *Service) Replace(userID string, webhook *smsgateway.WebhookDTO) error {
if webhook.ID == "" {
webhook.ID = s.idgen()
}

View File

@ -1,7 +0,0 @@
package webhooks
type Event string
const (
EventSmsReceived Event = "sms:received"
)

7
pkg/smsgateway/dto.go Normal file
View File

@ -0,0 +1,7 @@
package smsgateway
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 WebhookEvent `json:"event" validate:"required" example:"sms:received"`
}

7
pkg/smsgateway/types.go Normal file
View File

@ -0,0 +1,7 @@
package smsgateway
type WebhookEvent string
const (
WebhookEventSmsReceived WebhookEvent = "sms:received"
)