Skip to content

Commit 26e205d

Browse files
committed
[messages] add device last seen filter
1 parent 2152e60 commit 26e205d

File tree

6 files changed

+77
-35
lines changed

6 files changed

+77
-35
lines changed

internal/sms-gateway/handlers/messages/3rdparty.go

Lines changed: 40 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package messages
33
import (
44
"errors"
55
"fmt"
6+
"time"
67

78
"github.com/android-sms-gateway/client-go/smsgateway"
89
"github.com/android-sms-gateway/server/internal/sms-gateway/handlers/base"
@@ -44,49 +45,58 @@ type ThirdPartyController struct {
4445
// @Tags User, Messages
4546
// @Accept json
4647
// @Produce json
47-
// @Param skipPhoneValidation query bool false "Skip phone validation"
48-
// @Param request body smsgateway.Message true "Send message request"
49-
// @Success 202 {object} smsgateway.MessageState "Message enqueued"
50-
// @Failure 400 {object} smsgateway.ErrorResponse "Invalid request"
51-
// @Failure 401 {object} smsgateway.ErrorResponse "Unauthorized"
52-
// @Failure 409 {object} smsgateway.ErrorResponse "Message with such ID already exists"
53-
// @Failure 500 {object} smsgateway.ErrorResponse "Internal server error"
54-
// @Header 202 {string} Location "Get message state URL"
55-
// @Router /3rdparty/v1/messages [post]
48+
// @Param skipPhoneValidation query bool false "Skip phone validation"
49+
// @Param deviceActiveWithin query int false "Filter devices active within the specified number of hours" default(0)
50+
// @Param request body smsgateway.Message true "Send message request"
51+
// @Success 202 {object} smsgateway.MessageState "Message enqueued"
52+
// @Failure 400 {object} smsgateway.ErrorResponse "Invalid request"
53+
// @Failure 401 {object} smsgateway.ErrorResponse "Unauthorized"
54+
// @Failure 409 {object} smsgateway.ErrorResponse "Message with such ID already exists"
55+
// @Failure 500 {object} smsgateway.ErrorResponse "Internal server error"
56+
// @Header 202 {string} Location "Get message state URL"
57+
// @Router /3rdparty/v1/messages [post]
5658
//
5759
// Enqueue message
5860
func (h *ThirdPartyController) post(user models.User, c *fiber.Ctx) error {
59-
req := smsgateway.Message{}
60-
if err := h.BodyParserValidator(c, &req); err != nil {
61+
var params postQueryParams
62+
if err := h.QueryParserValidator(c, &params); err != nil {
6163
return fiber.NewError(fiber.StatusBadRequest, err.Error())
6264
}
6365

64-
skipPhoneValidation := c.QueryBool("skipPhoneValidation", false)
66+
var req smsgateway.Message
67+
if err := h.BodyParserValidator(c, &req); err != nil {
68+
return fiber.NewError(fiber.StatusBadRequest, err.Error())
69+
}
6570

6671
var device models.Device
6772
var err error
73+
var filters []devices.SelectFilter
74+
75+
if params.DeviceActiveWithin > 0 {
76+
filters = append(filters, devices.ActiveWithin(time.Duration(params.DeviceActiveWithin)*time.Hour))
77+
}
6878

6979
// Check if device_id is provided
7080
if req.DeviceID != "" {
71-
// Validate device ownership
72-
device, err = h.devicesSvc.Get(user.ID, devices.WithID(req.DeviceID))
81+
82+
device, err = h.devicesSvc.Get(user.ID, append(filters, devices.WithID(req.DeviceID))...)
7383
if err != nil {
7484
if errors.Is(err, devices.ErrNotFound) {
75-
return fiber.NewError(fiber.StatusBadRequest, "Invalid device ID")
85+
return fiber.NewError(fiber.StatusBadRequest, "No active device with such ID found")
7686
}
7787
h.Logger.Error("Failed to get device", zap.Error(err), zap.String("user_id", user.ID), zap.String("device_id", req.DeviceID))
7888
return fiber.NewError(fiber.StatusInternalServerError, "Can't select device. Please contact support")
7989
}
8090
} else {
8191
// Fallback to random selection
82-
devices, err := h.devicesSvc.Select(user.ID)
92+
devices, err := h.devicesSvc.Select(user.ID, filters...)
8393
if err != nil {
8494
h.Logger.Error("Failed to select devices", zap.Error(err), zap.String("user_id", user.ID))
8595
return fiber.NewError(fiber.StatusInternalServerError, "Can't select devices. Please contact support")
8696
}
8797

8898
if len(devices) < 1 {
89-
return fiber.NewError(fiber.StatusBadRequest, "No devices registered")
99+
return fiber.NewError(fiber.StatusBadRequest, "No active devices found")
90100
}
91101

92102
device, err = slices.Random(devices)
@@ -125,7 +135,7 @@ func (h *ThirdPartyController) post(user models.User, c *fiber.Ctx) error {
125135
ValidUntil: req.ValidUntil,
126136
Priority: req.Priority,
127137
}
128-
state, err := h.messagesSvc.Enqueue(device, msg, messages.EnqueueOptions{SkipPhoneValidation: skipPhoneValidation})
138+
state, err := h.messagesSvc.Enqueue(device, msg, messages.EnqueueOptions{SkipPhoneValidation: params.SkipPhoneValidation})
129139
if err != nil {
130140
var errValidation messages.ErrValidation
131141
if isBadRequest := errors.As(err, &errValidation); isBadRequest {
@@ -155,12 +165,12 @@ func (h *ThirdPartyController) post(user models.User, c *fiber.Ctx) error {
155165
// @Security ApiAuth
156166
// @Tags User, Messages
157167
// @Produce json
158-
// @Param id path string true "Message ID"
159-
// @Success 200 {object} smsgateway.MessageState "Message state"
160-
// @Failure 400 {object} smsgateway.ErrorResponse "Invalid request"
161-
// @Failure 401 {object} smsgateway.ErrorResponse "Unauthorized"
162-
// @Failure 500 {object} smsgateway.ErrorResponse "Internal server error"
163-
// @Router /3rdparty/v1/messages/{id} [get]
168+
// @Param id path string true "Message ID"
169+
// @Success 200 {object} smsgateway.MessageState "Message state"
170+
// @Failure 400 {object} smsgateway.ErrorResponse "Invalid request"
171+
// @Failure 401 {object} smsgateway.ErrorResponse "Unauthorized"
172+
// @Failure 500 {object} smsgateway.ErrorResponse "Internal server error"
173+
// @Router /3rdparty/v1/messages/{id} [get]
164174
//
165175
// Get message state
166176
func (h *ThirdPartyController) get(user models.User, c *fiber.Ctx) error {
@@ -184,12 +194,12 @@ func (h *ThirdPartyController) get(user models.User, c *fiber.Ctx) error {
184194
// @Tags User, Messages
185195
// @Accept json
186196
// @Produce json
187-
// @Param request body smsgateway.MessagesExportRequest true "Export inbox request"
188-
// @Success 202 {object} object "Inbox export request accepted"
189-
// @Failure 400 {object} smsgateway.ErrorResponse "Invalid request"
190-
// @Failure 401 {object} smsgateway.ErrorResponse "Unauthorized"
191-
// @Failure 500 {object} smsgateway.ErrorResponse "Internal server error"
192-
// @Router /3rdparty/v1/inbox/export [post]
197+
// @Param request body smsgateway.MessagesExportRequest true "Export inbox request"
198+
// @Success 202 {object} object "Inbox export request accepted"
199+
// @Failure 400 {object} smsgateway.ErrorResponse "Invalid request"
200+
// @Failure 401 {object} smsgateway.ErrorResponse "Unauthorized"
201+
// @Failure 500 {object} smsgateway.ErrorResponse "Internal server error"
202+
// @Router /3rdparty/v1/inbox/export [post]
193203
//
194204
// Export inbox
195205
func (h *ThirdPartyController) postInboxExport(user models.User, c *fiber.Ctx) error {
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package messages
2+
3+
type postQueryParams struct {
4+
SkipPhoneValidation bool `query:"skipPhoneValidation"`
5+
DeviceActiveWithin uint `query:"deviceActiveWithin"`
6+
}

internal/sms-gateway/modules/devices/repository_filter.go

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package devices
22

3-
import "gorm.io/gorm"
3+
import (
4+
"time"
5+
6+
"gorm.io/gorm"
7+
)
48

59
type SelectFilter func(*selectFilter)
610

@@ -22,10 +26,17 @@ func WithUserID(userID string) SelectFilter {
2226
}
2327
}
2428

29+
func ActiveWithin(duration time.Duration) SelectFilter {
30+
return func(f *selectFilter) {
31+
f.activeWithin = duration
32+
}
33+
}
34+
2535
type selectFilter struct {
26-
id *string
27-
userID *string
28-
token *string
36+
id *string
37+
userID *string
38+
token *string
39+
activeWithin time.Duration
2940
}
3041

3142
func newFilter(filters ...SelectFilter) *selectFilter {
@@ -50,5 +61,8 @@ func (f *selectFilter) apply(query *gorm.DB) *gorm.DB {
5061
if f.userID != nil {
5162
query = query.Where("user_id = ?", *f.userID)
5263
}
64+
if f.activeWithin != 0 {
65+
query = query.Where("last_seen > ?", time.Now().Add(-f.activeWithin))
66+
}
5367
return query
5468
}

pkg/swagger/docs/requests.http

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ GET {{baseUrl}}/health HTTP/1.1
1010
GET {{baseUrl}}/3rdparty/v1/health HTTP/1.1
1111

1212
###
13-
POST {{baseUrl}}/3rdparty/v1/messages?skipPhoneValidation=false HTTP/1.1
13+
POST {{baseUrl}}/3rdparty/v1/messages?skipPhoneValidation=false&deviceActiveWithin=240 HTTP/1.1
1414
Content-Type: application/json
1515
Authorization: Basic {{credentials}}
1616

pkg/swagger/docs/swagger.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,13 @@
290290
"name": "skipPhoneValidation",
291291
"in": "query"
292292
},
293+
{
294+
"type": "integer",
295+
"default": 0,
296+
"description": "Filter devices active within the specified number of hours",
297+
"name": "deviceActiveWithin",
298+
"in": "query"
299+
},
293300
{
294301
"description": "Send message request",
295302
"name": "request",

pkg/swagger/docs/swagger.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -900,6 +900,11 @@ paths:
900900
in: query
901901
name: skipPhoneValidation
902902
type: boolean
903+
- default: 0
904+
description: Filter devices active within the specified number of hours
905+
in: query
906+
name: deviceActiveWithin
907+
type: integer
903908
- description: Send message request
904909
in: body
905910
name: request

0 commit comments

Comments
 (0)