[webhooks] notify devices on change

This commit is contained in:
Aleksandr Soloshenko 2024-06-11 09:09:37 +07:00
parent d8af7e0c83
commit 79682075ff
21 changed files with 410 additions and 202 deletions

View File

@ -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": {

View File

@ -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
View File

@ -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
View File

@ -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=

View File

@ -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() {

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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))
}
}

View File

@ -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"

View File

@ -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]

View File

@ -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))
}

View File

@ -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,
),
)

View 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,
}
}

View 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
}

View 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"),
}
}

View File

@ -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)

View File

@ -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

View File

@ -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)))
}

View File

@ -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,
}
}

View File

@ -11,7 +11,6 @@ var Module = fx.Module(
return log.Named("repositories")
}),
fx.Provide(
NewDevicesRepository,
NewMessagesRepository,
NewUsersRepository,
),

View File

@ -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"),
}
}