diff --git a/api/mobile.http b/api/mobile.http new file mode 100644 index 0000000..e8e023e --- /dev/null +++ b/api/mobile.http @@ -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}} \ No newline at end of file diff --git a/api/requests.http b/api/requests.http index 366c805..599d65c 100644 --- a/api/requests.http +++ b/api/requests.http @@ -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 diff --git a/api/swagger.json b/api/swagger.json index c879f57..ba373b5 100644 --- a/api/swagger.json +++ b/api/swagger.json @@ -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": { diff --git a/api/swagger.yaml b/api/swagger.yaml index 42a981e..d698ea9 100644 --- a/api/swagger.yaml +++ b/api/swagger.yaml @@ -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: diff --git a/internal/sms-gateway/handlers/3rdparty.go b/internal/sms-gateway/handlers/3rdparty.go index c8be0a7..5701d33 100644 --- a/internal/sms-gateway/handlers/3rdparty.go +++ b/internal/sms-gateway/handlers/3rdparty.go @@ -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 diff --git a/internal/sms-gateway/handlers/mobile.go b/internal/sms-gateway/handlers/mobile.go index 4e53ad4..1e50b19 100644 --- a/internal/sms-gateway/handlers/mobile.go +++ b/internal/sms-gateway/handlers/mobile.go @@ -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, } } diff --git a/internal/sms-gateway/handlers/module.go b/internal/sms-gateway/handlers/module.go index 9e376f1..fa0b1b6 100644 --- a/internal/sms-gateway/handlers/module.go +++ b/internal/sms-gateway/handlers/module.go @@ -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, ), ) diff --git a/internal/sms-gateway/handlers/webhooks/3rdparty.go b/internal/sms-gateway/handlers/webhooks/3rdparty.go new file mode 100644 index 0000000..2d1622e --- /dev/null +++ b/internal/sms-gateway/handlers/webhooks/3rdparty.go @@ -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, + } +} diff --git a/internal/sms-gateway/handlers/webhooks/mobile.go b/internal/sms-gateway/handlers/webhooks/mobile.go new file mode 100644 index 0000000..029a856 --- /dev/null +++ b/internal/sms-gateway/handlers/webhooks/mobile.go @@ -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, + } +} diff --git a/internal/sms-gateway/modules/auth/decorators.go b/internal/sms-gateway/modules/auth/decorators.go index 8608aea..63dad97 100644 --- a/internal/sms-gateway/modules/auth/decorators.go +++ b/internal/sms-gateway/modules/auth/decorators.go @@ -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) + } +} diff --git a/internal/sms-gateway/modules/webhooks/converters.go b/internal/sms-gateway/modules/webhooks/converters.go index 795e661..da67126 100644 --- a/internal/sms-gateway/modules/webhooks/converters.go +++ b/internal/sms-gateway/modules/webhooks/converters.go @@ -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, diff --git a/internal/sms-gateway/modules/webhooks/dto.go b/internal/sms-gateway/modules/webhooks/dto.go deleted file mode 100644 index 869d559..0000000 --- a/internal/sms-gateway/modules/webhooks/dto.go +++ /dev/null @@ -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"` -} diff --git a/internal/sms-gateway/modules/webhooks/handler.go b/internal/sms-gateway/modules/webhooks/handler.go deleted file mode 100644 index 8b08a2a..0000000 --- a/internal/sms-gateway/modules/webhooks/handler.go +++ /dev/null @@ -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, - } -} diff --git a/internal/sms-gateway/modules/webhooks/models.go b/internal/sms-gateway/modules/webhooks/models.go index 663f5cd..deb205e 100644 --- a/internal/sms-gateway/modules/webhooks/models.go +++ b/internal/sms-gateway/modules/webhooks/models.go @@ -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"` diff --git a/internal/sms-gateway/modules/webhooks/module.go b/internal/sms-gateway/modules/webhooks/module.go index 8c0ed37..709c57b 100644 --- a/internal/sms-gateway/modules/webhooks/module.go +++ b/internal/sms-gateway/modules/webhooks/module.go @@ -14,7 +14,6 @@ var Module = fx.Module( fx.Provide(NewRepository, fx.Private), fx.Provide( NewService, - NewHandler, ), ) diff --git a/internal/sms-gateway/modules/webhooks/service.go b/internal/sms-gateway/modules/webhooks/service.go index e8ef2cc..d23a070 100644 --- a/internal/sms-gateway/modules/webhooks/service.go +++ b/internal/sms-gateway/modules/webhooks/service.go @@ -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() } diff --git a/internal/sms-gateway/modules/webhooks/types.go b/internal/sms-gateway/modules/webhooks/types.go deleted file mode 100644 index 475ec95..0000000 --- a/internal/sms-gateway/modules/webhooks/types.go +++ /dev/null @@ -1,7 +0,0 @@ -package webhooks - -type Event string - -const ( - EventSmsReceived Event = "sms:received" -) diff --git a/pkg/smsgateway/dto.go b/pkg/smsgateway/dto.go new file mode 100644 index 0000000..73b2054 --- /dev/null +++ b/pkg/smsgateway/dto.go @@ -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"` +} diff --git a/pkg/smsgateway/types.go b/pkg/smsgateway/types.go new file mode 100644 index 0000000..a60cc07 --- /dev/null +++ b/pkg/smsgateway/types.go @@ -0,0 +1,7 @@ +package smsgateway + +type WebhookEvent string + +const ( + WebhookEventSmsReceived WebhookEvent = "sms:received" +)