mirror of
https://github.com/makayabou/asg-server.git
synced 2026-05-02 17:43:36 +02:00
[webhooks] notify devices on change
This commit is contained in:
parent
d8af7e0c83
commit
79682075ff
115
api/swagger.json
115
api/swagger.json
@ -230,7 +230,7 @@
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/github_com_capcom6_sms-gateway_pkg_smsgateway.WebhookDTO"
|
||||
"$ref": "#/definitions/smsgateway.Webhook"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -273,7 +273,7 @@
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/github_com_capcom6_sms-gateway_pkg_smsgateway.WebhookDTO"
|
||||
"$ref": "#/definitions/smsgateway.Webhook"
|
||||
}
|
||||
}
|
||||
],
|
||||
@ -281,7 +281,7 @@
|
||||
"201": {
|
||||
"description": "Created",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/github_com_capcom6_sms-gateway_pkg_smsgateway.WebhookDTO"
|
||||
"$ref": "#/definitions/smsgateway.Webhook"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
@ -595,7 +595,7 @@
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/github_com_capcom6_sms-gateway_pkg_smsgateway.WebhookDTO"
|
||||
"$ref": "#/definitions/smsgateway.Webhook"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -668,41 +668,6 @@
|
||||
}
|
||||
},
|
||||
"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": {
|
||||
@ -1019,14 +984,46 @@
|
||||
"ProcessingStateFailed"
|
||||
]
|
||||
},
|
||||
"smsgateway.PushEventType": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"MessageEnqueued",
|
||||
"WebhooksUpdated"
|
||||
],
|
||||
"x-enum-varnames": [
|
||||
"PushMessageEnqueued",
|
||||
"PushWebhooksUpdated"
|
||||
]
|
||||
},
|
||||
"smsgateway.PushNotification": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"token"
|
||||
],
|
||||
"properties": {
|
||||
"data": {
|
||||
"description": "The additional data associated with the event.",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"event": {
|
||||
"description": "The type of event.",
|
||||
"default": "MessageEnqueued",
|
||||
"enum": [
|
||||
"MessageEnqueued",
|
||||
"WebhooksUpdated"
|
||||
],
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/smsgateway.PushEventType"
|
||||
}
|
||||
],
|
||||
"example": "MessageEnqueued"
|
||||
},
|
||||
"token": {
|
||||
"description": "Device FCM token",
|
||||
"description": "The token of the device that receives the notification.",
|
||||
"type": "string",
|
||||
"example": "PyDmBQZZXYmyxMwED8Fzy"
|
||||
}
|
||||
@ -1061,6 +1058,44 @@
|
||||
"example": "Pending"
|
||||
}
|
||||
}
|
||||
},
|
||||
"smsgateway.Webhook": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"event",
|
||||
"url"
|
||||
],
|
||||
"properties": {
|
||||
"event": {
|
||||
"description": "The type of event the webhook is triggered for.",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/smsgateway.WebhookEvent"
|
||||
}
|
||||
],
|
||||
"example": "sms:received"
|
||||
},
|
||||
"id": {
|
||||
"description": "The unique identifier of the webhook.",
|
||||
"type": "string",
|
||||
"maxLength": 36,
|
||||
"example": "123e4567-e89b-12d3-a456-426614174000"
|
||||
},
|
||||
"url": {
|
||||
"description": "The URL the webhook will be sent to.",
|
||||
"type": "string",
|
||||
"example": "https://example.com/webhook"
|
||||
}
|
||||
}
|
||||
},
|
||||
"smsgateway.WebhookEvent": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"sms:received"
|
||||
],
|
||||
"x-enum-varnames": [
|
||||
"WebhookEventSmsReceived"
|
||||
]
|
||||
}
|
||||
},
|
||||
"securityDefinitions": {
|
||||
|
||||
@ -1,28 +1,5 @@
|
||||
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:
|
||||
@ -261,10 +238,32 @@ definitions:
|
||||
- ProcessingStateSent
|
||||
- ProcessingStateDelivered
|
||||
- ProcessingStateFailed
|
||||
smsgateway.PushEventType:
|
||||
enum:
|
||||
- MessageEnqueued
|
||||
- WebhooksUpdated
|
||||
type: string
|
||||
x-enum-varnames:
|
||||
- PushMessageEnqueued
|
||||
- PushWebhooksUpdated
|
||||
smsgateway.PushNotification:
|
||||
properties:
|
||||
data:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: The additional data associated with the event.
|
||||
type: object
|
||||
event:
|
||||
allOf:
|
||||
- $ref: '#/definitions/smsgateway.PushEventType'
|
||||
default: MessageEnqueued
|
||||
description: The type of event.
|
||||
enum:
|
||||
- MessageEnqueued
|
||||
- WebhooksUpdated
|
||||
example: MessageEnqueued
|
||||
token:
|
||||
description: Device FCM token
|
||||
description: The token of the device that receives the notification.
|
||||
example: PyDmBQZZXYmyxMwED8Fzy
|
||||
type: string
|
||||
required:
|
||||
@ -291,6 +290,32 @@ definitions:
|
||||
- phoneNumber
|
||||
- state
|
||||
type: object
|
||||
smsgateway.Webhook:
|
||||
properties:
|
||||
event:
|
||||
allOf:
|
||||
- $ref: '#/definitions/smsgateway.WebhookEvent'
|
||||
description: The type of event the webhook is triggered for.
|
||||
example: sms:received
|
||||
id:
|
||||
description: The unique identifier of the webhook.
|
||||
example: 123e4567-e89b-12d3-a456-426614174000
|
||||
maxLength: 36
|
||||
type: string
|
||||
url:
|
||||
description: The URL the webhook will be sent to.
|
||||
example: https://example.com/webhook
|
||||
type: string
|
||||
required:
|
||||
- event
|
||||
- url
|
||||
type: object
|
||||
smsgateway.WebhookEvent:
|
||||
enum:
|
||||
- sms:received
|
||||
type: string
|
||||
x-enum-varnames:
|
||||
- WebhookEventSmsReceived
|
||||
host: sms.capcom.me
|
||||
info:
|
||||
contact:
|
||||
@ -435,7 +460,7 @@ paths:
|
||||
description: Webhook list
|
||||
schema:
|
||||
items:
|
||||
$ref: '#/definitions/github_com_capcom6_sms-gateway_pkg_smsgateway.WebhookDTO'
|
||||
$ref: '#/definitions/smsgateway.Webhook'
|
||||
type: array
|
||||
"401":
|
||||
description: Unauthorized
|
||||
@ -462,14 +487,14 @@ paths:
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/github_com_capcom6_sms-gateway_pkg_smsgateway.WebhookDTO'
|
||||
$ref: '#/definitions/smsgateway.Webhook'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"201":
|
||||
description: Created
|
||||
schema:
|
||||
$ref: '#/definitions/github_com_capcom6_sms-gateway_pkg_smsgateway.WebhookDTO'
|
||||
$ref: '#/definitions/smsgateway.Webhook'
|
||||
"400":
|
||||
description: Invalid request
|
||||
schema:
|
||||
@ -670,7 +695,7 @@ paths:
|
||||
description: Webhook list
|
||||
schema:
|
||||
items:
|
||||
$ref: '#/definitions/github_com_capcom6_sms-gateway_pkg_smsgateway.WebhookDTO'
|
||||
$ref: '#/definitions/smsgateway.Webhook'
|
||||
type: array
|
||||
"401":
|
||||
description: Unauthorized
|
||||
|
||||
2
go.mod
2
go.mod
@ -4,7 +4,7 @@ go 1.22.0
|
||||
|
||||
require (
|
||||
firebase.google.com/go/v4 v4.12.1
|
||||
github.com/android-sms-gateway/client-go v1.0.1-0.20240610220902-94dc5641aa00
|
||||
github.com/android-sms-gateway/client-go v1.0.1-0.20240610222412-894fc9370287
|
||||
github.com/capcom6/go-helpers v0.0.0-20240521035030-5f57bddeecee
|
||||
github.com/capcom6/go-infra-fx v0.0.2
|
||||
github.com/go-playground/validator/v10 v10.16.0
|
||||
|
||||
2
go.sum
2
go.sum
@ -34,6 +34,8 @@ github.com/android-sms-gateway/client-go v1.0.0 h1:TPRNHlgcEW6jThsx0y4AG1J7wH5Ir
|
||||
github.com/android-sms-gateway/client-go v1.0.0/go.mod h1:DQsReciU1xcaVW3T5Z2bqslNdsAwCFCtghawmA6g6L4=
|
||||
github.com/android-sms-gateway/client-go v1.0.1-0.20240610220902-94dc5641aa00 h1:GUtH5Pw57cxhpQ3y8EYQFTqbpjQ2dZR6G7BjcHK6lAM=
|
||||
github.com/android-sms-gateway/client-go v1.0.1-0.20240610220902-94dc5641aa00/go.mod h1:DQsReciU1xcaVW3T5Z2bqslNdsAwCFCtghawmA6g6L4=
|
||||
github.com/android-sms-gateway/client-go v1.0.1-0.20240610222412-894fc9370287 h1:4Q6TuWQcTrKb+nyMrdBTBIV0b4R/xQgmOJhOygHWIkg=
|
||||
github.com/android-sms-gateway/client-go v1.0.1-0.20240610222412-894fc9370287/go.mod h1:DQsReciU1xcaVW3T5Z2bqslNdsAwCFCtghawmA6g6L4=
|
||||
github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
|
||||
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
|
||||
@ -13,12 +13,12 @@ import (
|
||||
"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/devices"
|
||||
"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"
|
||||
"go.uber.org/fx/fxevent"
|
||||
"go.uber.org/zap"
|
||||
@ -33,7 +33,6 @@ var Module = fx.Module(
|
||||
http.Module,
|
||||
validator.Module,
|
||||
handlers.Module,
|
||||
services.Module,
|
||||
auth.Module,
|
||||
push.Module,
|
||||
repositories.Module,
|
||||
@ -41,6 +40,7 @@ var Module = fx.Module(
|
||||
messages.Module,
|
||||
health.Module,
|
||||
webhooks.Module,
|
||||
devices.Module,
|
||||
)
|
||||
|
||||
func Run() {
|
||||
|
||||
@ -9,9 +9,9 @@ import (
|
||||
"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/devices"
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/modules/messages"
|
||||
"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"
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
@ -32,7 +32,7 @@ type ThirdPartyHandlerParams struct {
|
||||
|
||||
AuthSvc *auth.Service
|
||||
MessagesSvc *messages.Service
|
||||
DevicesSvc *services.DevicesService
|
||||
DevicesSvc *devices.Service
|
||||
|
||||
Logger *zap.Logger
|
||||
Validator *validator.Validate
|
||||
@ -46,7 +46,7 @@ type thirdPartyHandler struct {
|
||||
|
||||
authSvc *auth.Service
|
||||
messagesSvc *messages.Service
|
||||
devicesSvc *services.DevicesService
|
||||
devicesSvc *devices.Service
|
||||
}
|
||||
|
||||
// @Summary List devices
|
||||
@ -62,7 +62,7 @@ type thirdPartyHandler struct {
|
||||
//
|
||||
// List devices
|
||||
func (h *thirdPartyHandler) getDevice(user models.User, c *fiber.Ctx) error {
|
||||
devices, err := h.devicesSvc.Select(user)
|
||||
devices, err := h.devicesSvc.Select(devices.WithUserID(user.ID))
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't select devices: %w", err)
|
||||
}
|
||||
@ -110,7 +110,7 @@ func (h *thirdPartyHandler) postMessage(user models.User, c *fiber.Ctx) error {
|
||||
|
||||
skipPhoneValidation := c.QueryBool("skipPhoneValidation", false)
|
||||
|
||||
devices, err := h.devicesSvc.Select(user)
|
||||
devices, err := h.devicesSvc.Select(devices.WithUserID(user.ID))
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't select devices: %w", err)
|
||||
}
|
||||
|
||||
@ -62,7 +62,7 @@ func (h *mobileHandler) postDevice(c *fiber.Ctx) error {
|
||||
return fmt.Errorf("can't create user: %w", err)
|
||||
}
|
||||
|
||||
device, err := h.authSvc.RegisterDevice(user.ID, req.Name, req.PushToken)
|
||||
device, err := h.authSvc.RegisterDevice(user, req.Name, req.PushToken)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't register device: %w", err)
|
||||
}
|
||||
|
||||
@ -73,7 +73,7 @@ func (h *upstreamHandler) postPush(c *fiber.Ctx) error {
|
||||
Data: v.Data,
|
||||
}
|
||||
|
||||
if err := h.pushSvc.Enqueue(c.Context(), v.Token, &event); err != nil {
|
||||
if err := h.pushSvc.Enqueue(v.Token, &event); err != nil {
|
||||
h.Logger.Error("Can't push message", zap.Error(err))
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,7 +34,7 @@ type ThirdPartyController struct {
|
||||
// @Security ApiAuth
|
||||
// @Tags User, Webhooks
|
||||
// @Produce json
|
||||
// @Success 200 {object} []smsgateway.WebhookDTO "Webhook list"
|
||||
// @Success 200 {object} []smsgateway.Webhook "Webhook list"
|
||||
// @Failure 401 {object} smsgateway.ErrorResponse "Unauthorized"
|
||||
// @Failure 500 {object} smsgateway.ErrorResponse "Internal server error"
|
||||
// @Router /api/v1/3rdparty/webhooks [get]
|
||||
@ -55,8 +55,8 @@ func (h *ThirdPartyController) get(user models.User, c *fiber.Ctx) error {
|
||||
// @Tags User, Webhooks
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body smsgateway.WebhookDTO true "Webhook"
|
||||
// @Success 201 {object} smsgateway.WebhookDTO "Created"
|
||||
// @Param request body smsgateway.Webhook true "Webhook"
|
||||
// @Success 201 {object} smsgateway.Webhook "Created"
|
||||
// @Failure 400 {object} smsgateway.ErrorResponse "Invalid request"
|
||||
// @Failure 401 {object} smsgateway.ErrorResponse "Unauthorized"
|
||||
// @Failure 500 {object} smsgateway.ErrorResponse "Internal server error"
|
||||
|
||||
@ -31,7 +31,7 @@ type MobileController struct {
|
||||
// @Security MobileToken
|
||||
// @Tags Device, Webhooks
|
||||
// @Produce json
|
||||
// @Success 200 {object} []smsgateway.WebhookDTO "Webhook list"
|
||||
// @Success 200 {object} []smsgateway.Webhook "Webhook list"
|
||||
// @Failure 401 {object} smsgateway.ErrorResponse "Unauthorized"
|
||||
// @Failure 500 {object} smsgateway.ErrorResponse "Internal server error"
|
||||
// @Router /mobile/v1/webhooks [get]
|
||||
|
||||
@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/models"
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/modules/devices"
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/repositories"
|
||||
"github.com/capcom6/sms-gateway/pkg/crypto"
|
||||
"github.com/jaevor/go-nanoid"
|
||||
@ -21,8 +22,8 @@ type Params struct {
|
||||
|
||||
Config Config
|
||||
|
||||
Users *repositories.UsersRepository
|
||||
Devices *repositories.DevicesRepository
|
||||
Users *repositories.UsersRepository
|
||||
DevicesSvc *devices.Service
|
||||
|
||||
Logger *zap.Logger
|
||||
}
|
||||
@ -30,8 +31,8 @@ type Params struct {
|
||||
type Service struct {
|
||||
config Config
|
||||
|
||||
users *repositories.UsersRepository
|
||||
devices *repositories.DevicesRepository
|
||||
users *repositories.UsersRepository
|
||||
devicesSvc *devices.Service
|
||||
|
||||
logger *zap.Logger
|
||||
|
||||
@ -42,11 +43,11 @@ func New(params Params) *Service {
|
||||
idgen, _ := nanoid.Standard(21)
|
||||
|
||||
return &Service{
|
||||
config: params.Config,
|
||||
users: params.Users,
|
||||
devices: params.Devices,
|
||||
logger: params.Logger.Named("Service"),
|
||||
idgen: idgen,
|
||||
config: params.Config,
|
||||
users: params.Users,
|
||||
devicesSvc: params.DevicesSvc,
|
||||
logger: params.Logger.Named("Service"),
|
||||
idgen: idgen,
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,20 +68,17 @@ func (s *Service) RegisterUser(login, password string) (models.User, error) {
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (s *Service) RegisterDevice(userID string, name, pushToken *string) (models.Device, error) {
|
||||
func (s *Service) RegisterDevice(user models.User, name, pushToken *string) (models.Device, error) {
|
||||
device := models.Device{
|
||||
ID: s.idgen(),
|
||||
Name: name,
|
||||
AuthToken: s.idgen(),
|
||||
PushToken: pushToken,
|
||||
UserID: userID,
|
||||
}
|
||||
|
||||
return device, s.devices.Insert(&device)
|
||||
return device, s.devicesSvc.Insert(user, &device)
|
||||
}
|
||||
|
||||
func (s *Service) UpdateDevice(id, pushToken string) error {
|
||||
return s.devices.UpdateToken(id, pushToken)
|
||||
return s.devicesSvc.UpdateToken(id, pushToken)
|
||||
}
|
||||
|
||||
func (s *Service) IsPublic() bool {
|
||||
@ -100,12 +98,12 @@ func (s *Service) AuthorizeRegistration(token string) error {
|
||||
}
|
||||
|
||||
func (s *Service) AuthorizeDevice(token string) (models.Device, error) {
|
||||
device, err := s.devices.GetByToken(token)
|
||||
device, err := s.devicesSvc.Get(devices.WithToken(token))
|
||||
if err != nil {
|
||||
return device, err
|
||||
}
|
||||
|
||||
if err := s.devices.UpdateLastSeen(device.ID); err != nil {
|
||||
if err := s.devicesSvc.UpdateLastSeen(device.ID); err != nil {
|
||||
s.logger.Error("can't update last seen", zap.Error(err))
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package services
|
||||
package devices
|
||||
|
||||
import (
|
||||
"go.uber.org/fx"
|
||||
@ -6,11 +6,15 @@ import (
|
||||
)
|
||||
|
||||
var Module = fx.Module(
|
||||
"services",
|
||||
"devices",
|
||||
fx.Decorate(func(log *zap.Logger) *zap.Logger {
|
||||
return log.Named("services")
|
||||
return log.Named("devices")
|
||||
}),
|
||||
fx.Provide(
|
||||
NewDevicesService,
|
||||
newDevicesRepository,
|
||||
fx.Private,
|
||||
),
|
||||
fx.Provide(
|
||||
NewService,
|
||||
),
|
||||
)
|
||||
65
internal/sms-gateway/modules/devices/repository.go
Normal file
65
internal/sms-gateway/modules/devices/repository.go
Normal file
@ -0,0 +1,65 @@
|
||||
package devices
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/models"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNotFound = gorm.ErrRecordNotFound
|
||||
ErrInvalidFilter = errors.New("invalid filter")
|
||||
ErrMoreThanOne = errors.New("more than one record")
|
||||
)
|
||||
|
||||
type repository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
func (r *repository) Select(filter ...SelectFilter) ([]models.Device, error) {
|
||||
if len(filter) == 0 {
|
||||
return nil, ErrInvalidFilter
|
||||
}
|
||||
|
||||
f := newFilter(filter...)
|
||||
devices := []models.Device{}
|
||||
|
||||
return devices, f.apply(r.db).Find(&devices).Error
|
||||
}
|
||||
|
||||
func (r *repository) Get(filter ...SelectFilter) (models.Device, error) {
|
||||
devices, err := r.Select(filter...)
|
||||
if err != nil {
|
||||
return models.Device{}, err
|
||||
}
|
||||
|
||||
if len(devices) == 0 {
|
||||
return models.Device{}, ErrNotFound
|
||||
}
|
||||
|
||||
if len(devices) > 1 {
|
||||
return models.Device{}, ErrMoreThanOne
|
||||
}
|
||||
|
||||
return devices[0], nil
|
||||
}
|
||||
|
||||
func (r *repository) Insert(device *models.Device) error {
|
||||
return r.db.Create(device).Error
|
||||
}
|
||||
|
||||
func (r *repository) UpdateToken(id, token string) error {
|
||||
return r.db.Model(&models.Device{}).Where("id", id).Update("push_token", token).Error
|
||||
}
|
||||
|
||||
func (r *repository) UpdateLastSeen(id string) error {
|
||||
return r.db.Model(&models.Device{}).Where("id", id).Update("last_seen", time.Now()).Error
|
||||
}
|
||||
|
||||
func newDevicesRepository(db *gorm.DB) *repository {
|
||||
return &repository{
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
54
internal/sms-gateway/modules/devices/repository_filter.go
Normal file
54
internal/sms-gateway/modules/devices/repository_filter.go
Normal file
@ -0,0 +1,54 @@
|
||||
package devices
|
||||
|
||||
import "gorm.io/gorm"
|
||||
|
||||
type SelectFilter func(*selectFilter)
|
||||
|
||||
func WithID(id string) SelectFilter {
|
||||
return func(f *selectFilter) {
|
||||
f.id = &id
|
||||
}
|
||||
}
|
||||
|
||||
func WithToken(token string) SelectFilter {
|
||||
return func(f *selectFilter) {
|
||||
f.token = &token
|
||||
}
|
||||
}
|
||||
|
||||
func WithUserID(userID string) SelectFilter {
|
||||
return func(f *selectFilter) {
|
||||
f.userID = &userID
|
||||
}
|
||||
}
|
||||
|
||||
type selectFilter struct {
|
||||
id *string
|
||||
userID *string
|
||||
token *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 {
|
||||
if f.id != nil {
|
||||
query = query.Where("id = ?", *f.id)
|
||||
}
|
||||
if f.token != nil {
|
||||
query = query.Where("auth_token = ?", *f.token)
|
||||
}
|
||||
if f.userID != nil {
|
||||
query = query.Where("user_id = ?", *f.userID)
|
||||
}
|
||||
return query
|
||||
}
|
||||
58
internal/sms-gateway/modules/devices/service.go
Normal file
58
internal/sms-gateway/modules/devices/service.go
Normal file
@ -0,0 +1,58 @@
|
||||
package devices
|
||||
|
||||
import (
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/models"
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/modules/db"
|
||||
"go.uber.org/fx"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type ServiceParams struct {
|
||||
fx.In
|
||||
|
||||
Devices *repository
|
||||
|
||||
IDGen db.IDGen
|
||||
|
||||
Logger *zap.Logger
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
devices *repository
|
||||
|
||||
idGen db.IDGen
|
||||
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
func (s *Service) Insert(user models.User, device *models.Device) error {
|
||||
device.ID = s.idGen()
|
||||
device.AuthToken = s.idGen()
|
||||
device.UserID = user.ID
|
||||
|
||||
return s.devices.Insert(device)
|
||||
}
|
||||
|
||||
func (s *Service) Select(filter ...SelectFilter) ([]models.Device, error) {
|
||||
return s.devices.Select(filter...)
|
||||
}
|
||||
|
||||
func (s *Service) Get(filter ...SelectFilter) (models.Device, error) {
|
||||
return s.devices.Get(filter...)
|
||||
}
|
||||
|
||||
func (s *Service) UpdateToken(deviceId string, token string) error {
|
||||
return s.devices.UpdateToken(deviceId, token)
|
||||
}
|
||||
|
||||
func (s *Service) UpdateLastSeen(deviceId string) error {
|
||||
return s.devices.UpdateLastSeen(deviceId)
|
||||
}
|
||||
|
||||
func NewService(params ServiceParams) *Service {
|
||||
return &Service{
|
||||
devices: params.Devices,
|
||||
idGen: params.IDGen,
|
||||
logger: params.Logger.Named("service"),
|
||||
}
|
||||
}
|
||||
@ -214,10 +214,7 @@ func (s *Service) Enqeue(device models.Device, message smsgateway.Message, opts
|
||||
}
|
||||
|
||||
go func(token string) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if err := s.PushSvc.Enqueue(ctx, token, push.NewMessageEnqueuedEvent()); err != nil {
|
||||
if err := s.PushSvc.Enqueue(token, push.NewMessageEnqueuedEvent()); err != nil {
|
||||
s.Logger.Error("Can't enqueue message", zap.String("token", token), zap.Error(err))
|
||||
}
|
||||
}(*device.PushToken)
|
||||
|
||||
@ -70,7 +70,7 @@ func (s *Service) Run(ctx context.Context) {
|
||||
}
|
||||
|
||||
// Enqueue adds the data to the cache and immediately sends all messages if the debounce is 0.
|
||||
func (s *Service) Enqueue(ctx context.Context, token string, event *Event) error {
|
||||
func (s *Service) Enqueue(token string, event *Event) error {
|
||||
s.cache.Set(token, event.Map())
|
||||
|
||||
return nil
|
||||
|
||||
@ -6,18 +6,43 @@ import (
|
||||
"github.com/android-sms-gateway/client-go/smsgateway"
|
||||
"github.com/capcom6/go-helpers/slices"
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/modules/db"
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/modules/devices"
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/modules/push"
|
||||
"go.uber.org/fx"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type ServiceParams struct {
|
||||
fx.In
|
||||
|
||||
idgen db.IDGen
|
||||
|
||||
webhooks *Repository
|
||||
|
||||
devicesSvc *devices.Service
|
||||
pushSvc *push.Service
|
||||
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
idgen db.IDGen
|
||||
|
||||
webhooks *Repository
|
||||
|
||||
devicesSvc *devices.Service
|
||||
pushSvc *push.Service
|
||||
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
func NewService(idgen db.IDGen, webhooks *Repository) *Service {
|
||||
func NewService(params ServiceParams) *Service {
|
||||
return &Service{
|
||||
idgen: idgen,
|
||||
webhooks: webhooks,
|
||||
idgen: params.idgen,
|
||||
webhooks: params.webhooks,
|
||||
devicesSvc: params.devicesSvc,
|
||||
pushSvc: params.pushSvc,
|
||||
logger: params.logger,
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,10 +69,44 @@ func (s *Service) Replace(userID string, webhook *smsgateway.Webhook) error {
|
||||
Event: webhook.Event,
|
||||
}
|
||||
|
||||
return s.webhooks.Replace(&model)
|
||||
if err := s.webhooks.Replace(&model); err != nil {
|
||||
return fmt.Errorf("can't replace webhook: %w", err)
|
||||
}
|
||||
|
||||
go s.notifyDevices(userID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) Delete(userID string, filters ...SelectFilter) error {
|
||||
filters = append(filters, WithUserID(userID))
|
||||
return s.webhooks.Delete(filters...)
|
||||
if err := s.webhooks.Delete(filters...); err != nil {
|
||||
return fmt.Errorf("can't delete webhooks: %w", err)
|
||||
}
|
||||
|
||||
go s.notifyDevices(userID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) notifyDevices(userID string) {
|
||||
s.logger.Info("Notifying devices", zap.String("user_id", userID))
|
||||
|
||||
devices, err := s.devicesSvc.Select(devices.WithUserID(userID))
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to select devices", zap.String("user_id", userID), zap.Error(err))
|
||||
return
|
||||
}
|
||||
|
||||
for _, device := range devices {
|
||||
if device.PushToken == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := s.pushSvc.Enqueue(*device.PushToken, push.NewWebhooksUpdatedEvent()); err != nil {
|
||||
s.logger.Error("Failed to send push notification", zap.String("user_id", userID), zap.Error(err))
|
||||
}
|
||||
}
|
||||
|
||||
s.logger.Info("Notified devices", zap.String("user_id", userID), zap.Int("count", len(devices)))
|
||||
}
|
||||
|
||||
@ -1,56 +0,0 @@
|
||||
package repositories
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/models"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrDeviceNotFound = gorm.ErrRecordNotFound
|
||||
)
|
||||
|
||||
type SelectDevicesFilter struct {
|
||||
UserId *string
|
||||
}
|
||||
|
||||
type DevicesRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
func (r *DevicesRepository) Select(filter SelectDevicesFilter) ([]models.Device, error) {
|
||||
devices := []models.Device{}
|
||||
|
||||
return devices, r.db.Where(filter).Find(&devices).Error
|
||||
}
|
||||
|
||||
func (r *DevicesRepository) Get(id string) (models.Device, error) {
|
||||
device := models.Device{}
|
||||
|
||||
return device, r.db.Where("id = ?", id).Take(&device).Error
|
||||
}
|
||||
|
||||
func (r *DevicesRepository) GetByToken(token string) (models.Device, error) {
|
||||
device := models.Device{}
|
||||
|
||||
return device, r.db.Where("auth_token = ?", token).Take(&device).Error
|
||||
}
|
||||
|
||||
func (r *DevicesRepository) Insert(device *models.Device) error {
|
||||
return r.db.Create(device).Error
|
||||
}
|
||||
|
||||
func (r *DevicesRepository) UpdateToken(id, token string) error {
|
||||
return r.db.Model(&models.Device{}).Where("id", id).Update("push_token", token).Error
|
||||
}
|
||||
|
||||
func (r *DevicesRepository) UpdateLastSeen(id string) error {
|
||||
return r.db.Model(&models.Device{}).Where("id", id).Update("last_seen", time.Now()).Error
|
||||
}
|
||||
|
||||
func NewDevicesRepository(db *gorm.DB) *DevicesRepository {
|
||||
return &DevicesRepository{
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
@ -11,7 +11,6 @@ var Module = fx.Module(
|
||||
return log.Named("repositories")
|
||||
}),
|
||||
fx.Provide(
|
||||
NewDevicesRepository,
|
||||
NewMessagesRepository,
|
||||
NewUsersRepository,
|
||||
),
|
||||
|
||||
@ -1,32 +0,0 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/models"
|
||||
"github.com/capcom6/sms-gateway/internal/sms-gateway/repositories"
|
||||
"go.uber.org/fx"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type DevicesServiceParams struct {
|
||||
fx.In
|
||||
|
||||
Devices *repositories.DevicesRepository
|
||||
Logger *zap.Logger
|
||||
}
|
||||
|
||||
type DevicesService struct {
|
||||
Devices *repositories.DevicesRepository
|
||||
|
||||
Logger *zap.Logger
|
||||
}
|
||||
|
||||
func (s *DevicesService) Select(user models.User) ([]models.Device, error) {
|
||||
return s.Devices.Select(repositories.SelectDevicesFilter{UserId: &user.ID})
|
||||
}
|
||||
|
||||
func NewDevicesService(params DevicesServiceParams) *DevicesService {
|
||||
return &DevicesService{
|
||||
Devices: params.Devices,
|
||||
Logger: params.Logger.Named("DevicesService"),
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user