Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ toolchain go1.23.2

require (
firebase.google.com/go/v4 v4.12.1
github.com/android-sms-gateway/client-go v1.5.8
github.com/android-sms-gateway/client-go v1.6.0
github.com/ansrivas/fiberprometheus/v2 v2.6.1
github.com/capcom6/go-helpers v0.2.0
github.com/capcom6/go-infra-fx v0.2.1
Expand Down
14 changes: 8 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,14 @@ github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migc
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
github.com/android-sms-gateway/client-go v1.5.7 h1:1L9Ot3yc+5DtGaDOCUj4/8DEECWyfo4IoPyL+oXnzyE=
github.com/android-sms-gateway/client-go v1.5.7/go.mod h1:DQsReciU1xcaVW3T5Z2bqslNdsAwCFCtghawmA6g6L4=
github.com/android-sms-gateway/client-go v1.5.8-0.20250516025314-5876d8deb355 h1:fctR5OH1c7g1zWEfp4K+fCZkY4+tZwTiKr/rN5N2yS8=
github.com/android-sms-gateway/client-go v1.5.8-0.20250516025314-5876d8deb355/go.mod h1:DQsReciU1xcaVW3T5Z2bqslNdsAwCFCtghawmA6g6L4=
github.com/android-sms-gateway/client-go v1.5.8 h1:t9630c1Hv8u/MjwQ8epJ0iDpt3VXurSNFC91CFEjM/M=
github.com/android-sms-gateway/client-go v1.5.8/go.mod h1:DQsReciU1xcaVW3T5Z2bqslNdsAwCFCtghawmA6g6L4=
github.com/android-sms-gateway/client-go v1.5.9-0.20250522134006-6e8b4dd3057a h1:TSmfm+KOsR1Ie10nZEjCVDepa1bEPin0NAgEUOSJiqw=
github.com/android-sms-gateway/client-go v1.5.9-0.20250522134006-6e8b4dd3057a/go.mod h1:DQsReciU1xcaVW3T5Z2bqslNdsAwCFCtghawmA6g6L4=
github.com/android-sms-gateway/client-go v1.5.9-0.20250522231449-9e0855eff19f h1:VYrL6YbkQ49pcyiXTYcR5LN1WpNy1Tc684XjeE1UCvw=
github.com/android-sms-gateway/client-go v1.5.9-0.20250522231449-9e0855eff19f/go.mod h1:DQsReciU1xcaVW3T5Z2bqslNdsAwCFCtghawmA6g6L4=
github.com/android-sms-gateway/client-go v1.5.9-0.20250524095300-2e41cae07049 h1:kdyVkqrgKDSI13JOKXVFz1al3IxfJPcbUaJvSXF6z+0=
github.com/android-sms-gateway/client-go v1.5.9-0.20250524095300-2e41cae07049/go.mod h1:DQsReciU1xcaVW3T5Z2bqslNdsAwCFCtghawmA6g6L4=
github.com/android-sms-gateway/client-go v1.6.0 h1:3hN0XEUnNrweBl5Xx3IfE5zyq5ihm7fB0dhuTZBKlns=
github.com/android-sms-gateway/client-go v1.6.0/go.mod h1:DQsReciU1xcaVW3T5Z2bqslNdsAwCFCtghawmA6g6L4=
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
github.com/ansrivas/fiberprometheus/v2 v2.6.1 h1:wac3pXaE6BYYTF04AC6K0ktk6vCD+MnDOJZ3SK66kXM=
Expand Down
2 changes: 2 additions & 0 deletions internal/sms-gateway/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/android-sms-gateway/server/internal/sms-gateway/modules/messages"
"github.com/android-sms-gateway/server/internal/sms-gateway/modules/metrics"
"github.com/android-sms-gateway/server/internal/sms-gateway/modules/push"
"github.com/android-sms-gateway/server/internal/sms-gateway/modules/settings"
"github.com/android-sms-gateway/server/internal/sms-gateway/modules/webhooks"
"github.com/capcom6/go-infra-fx/cli"
"github.com/capcom6/go-infra-fx/db"
Expand All @@ -40,6 +41,7 @@ var Module = fx.Module(
messages.Module,
health.Module,
webhooks.Module,
settings.Module,
devices.Module,
metrics.Module,
cleaner.Module,
Expand Down
6 changes: 6 additions & 0 deletions internal/sms-gateway/handlers/3rdparty.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/android-sms-gateway/server/internal/sms-gateway/handlers/logs"
"github.com/android-sms-gateway/server/internal/sms-gateway/handlers/messages"
"github.com/android-sms-gateway/server/internal/sms-gateway/handlers/middlewares/userauth"
"github.com/android-sms-gateway/server/internal/sms-gateway/handlers/settings"
"github.com/android-sms-gateway/server/internal/sms-gateway/handlers/webhooks"
"github.com/android-sms-gateway/server/internal/sms-gateway/modules/auth"
"github.com/go-playground/validator/v10"
Expand All @@ -21,6 +22,7 @@ type ThirdPartyHandlerParams struct {
MessagesHandler *messages.ThirdPartyController
WebhooksHandler *webhooks.ThirdPartyController
DevicesHandler *devices.ThirdPartyController
SettingsHandler *settings.ThirdPartyController
LogsHandler *logs.ThirdPartyController

AuthSvc *auth.Service
Expand All @@ -36,6 +38,7 @@ type thirdPartyHandler struct {
messagesHandler *messages.ThirdPartyController
webhooksHandler *webhooks.ThirdPartyController
devicesHandler *devices.ThirdPartyController
settingsHandler *settings.ThirdPartyController
logsHandler *logs.ThirdPartyController

authSvc *auth.Service
Expand All @@ -57,6 +60,8 @@ func (h *thirdPartyHandler) Register(router fiber.Router) {
h.devicesHandler.Register(router.Group("/device")) // TODO: remove after 2025-07-11
h.devicesHandler.Register(router.Group("/devices"))

h.settingsHandler.Register(router.Group("/settings"))

h.webhooksHandler.Register(router.Group("/webhooks"))

h.logsHandler.Register(router.Group("/logs"))
Expand All @@ -69,6 +74,7 @@ func newThirdPartyHandler(params ThirdPartyHandlerParams) *thirdPartyHandler {
messagesHandler: params.MessagesHandler,
webhooksHandler: params.WebhooksHandler,
devicesHandler: params.DevicesHandler,
settingsHandler: params.SettingsHandler,
logsHandler: params.LogsHandler,
authSvc: params.AuthSvc,
}
Expand Down
8 changes: 5 additions & 3 deletions internal/sms-gateway/handlers/converters/devices_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@ func TestDeviceToDTO(t *testing.T) {
ID: "test-id",
Name: anys.AsPointer("test-name"),
LastSeen: lastSeenAt,
TimedModel: models.TimedModel{
CreatedAt: createdAt,
UpdatedAt: updatedAt,
SoftDeletableModel: models.SoftDeletableModel{
TimedModel: models.TimedModel{
CreatedAt: createdAt,
UpdatedAt: updatedAt,
},
},
},
expected: smsgateway.Device{
Expand Down
6 changes: 6 additions & 0 deletions internal/sms-gateway/handlers/mobile.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/android-sms-gateway/server/internal/sms-gateway/handlers/converters"
"github.com/android-sms-gateway/server/internal/sms-gateway/handlers/middlewares/deviceauth"
"github.com/android-sms-gateway/server/internal/sms-gateway/handlers/middlewares/userauth"
"github.com/android-sms-gateway/server/internal/sms-gateway/handlers/settings"
"github.com/android-sms-gateway/server/internal/sms-gateway/handlers/webhooks"
"github.com/android-sms-gateway/server/internal/sms-gateway/models"
"github.com/android-sms-gateway/server/internal/sms-gateway/modules/auth"
Expand All @@ -33,6 +34,7 @@ type mobileHandler struct {
messagesSvc *messages.Service

webhooksCtrl *webhooks.MobileController
settingsCtrl *settings.MobileController

idGen func() string
}
Expand Down Expand Up @@ -303,6 +305,8 @@ func (h *mobileHandler) Register(router fiber.Router) {
router.Patch("/user/password", deviceauth.WithDevice(h.changePassword))

h.webhooksCtrl.Register(router.Group("/webhooks"))

h.settingsCtrl.Register(router.Group("/settings"))
}

type mobileHandlerParams struct {
Expand All @@ -316,6 +320,7 @@ type mobileHandlerParams struct {
MessagesSvc *messages.Service

WebhooksCtrl *webhooks.MobileController
SettingsCtrl *settings.MobileController
}

func newMobileHandler(params mobileHandlerParams) *mobileHandler {
Expand All @@ -327,6 +332,7 @@ func newMobileHandler(params mobileHandlerParams) *mobileHandler {
devicesSvc: params.DevicesSvc,
messagesSvc: params.MessagesSvc,
webhooksCtrl: params.WebhooksCtrl,
settingsCtrl: params.SettingsCtrl,
idGen: idGen,
}
}
3 changes: 3 additions & 0 deletions internal/sms-gateway/handlers/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"github.com/android-sms-gateway/server/internal/sms-gateway/handlers/devices"
"github.com/android-sms-gateway/server/internal/sms-gateway/handlers/logs"
"github.com/android-sms-gateway/server/internal/sms-gateway/handlers/messages"
"github.com/android-sms-gateway/server/internal/sms-gateway/handlers/settings"
"github.com/android-sms-gateway/server/internal/sms-gateway/handlers/webhooks"
"github.com/capcom6/go-infra-fx/http"
"go.uber.org/fx"
Expand All @@ -27,6 +28,8 @@ var Module = fx.Module(
webhooks.NewThirdPartyController,
webhooks.NewMobileController,
devices.NewThirdPartyController,
settings.NewThirdPartyController,
settings.NewMobileController,
logs.NewThirdPartyController,
fx.Private,
),
Expand Down
137 changes: 137 additions & 0 deletions internal/sms-gateway/handlers/settings/3rdparty.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package settings

import (
"fmt"

"github.com/android-sms-gateway/client-go/smsgateway"
"github.com/android-sms-gateway/server/internal/sms-gateway/handlers/base"
"github.com/android-sms-gateway/server/internal/sms-gateway/handlers/middlewares/userauth"
"github.com/android-sms-gateway/server/internal/sms-gateway/models"
"github.com/android-sms-gateway/server/internal/sms-gateway/modules/devices"
"github.com/android-sms-gateway/server/internal/sms-gateway/modules/settings"
"github.com/go-playground/validator/v10"
"github.com/gofiber/fiber/v2"
"go.uber.org/fx"
"go.uber.org/zap"
)

type thirdPartyControllerParams struct {
fx.In

DevicesSvc *devices.Service
SettingsSvc *settings.Service

Validator *validator.Validate
Logger *zap.Logger
}

type ThirdPartyController struct {
base.Handler

devicesSvc *devices.Service
settingsSvc *settings.Service
}

// @Summary Get settings
// @Description Returns settings for a specific user
// @Security ApiAuth
// @Tags User, Settings
// @Produce json
// @Success 200 {object} smsgateway.DeviceSettings "Settings"
// @Failure 401 {object} smsgateway.ErrorResponse "Unauthorized"
// @Failure 500 {object} smsgateway.ErrorResponse "Internal server error"
// @Router /3rdparty/v1/settings [get]
//
// Get settings
func (h *ThirdPartyController) get(user models.User, c *fiber.Ctx) error {
settings, err := h.settingsSvc.GetSettings(user.ID, true)
if err != nil {
return fmt.Errorf("can't get settings: %w", err)
}

return c.JSON(settings)
}

// @Summary Replace settings
// @Description Replaces settings
// @Security ApiAuth
// @Tags User, Settings
// @Accept json
// @Produce json
// @Param request body smsgateway.DeviceSettings true "Settings"
// @Success 200 {object} object "Settings updated"
// @Failure 400 {object} smsgateway.ErrorResponse "Invalid request"
// @Failure 401 {object} smsgateway.ErrorResponse "Unauthorized"
// @Failure 500 {object} smsgateway.ErrorResponse "Internal server error"
// @Router /3rdparty/v1/settings [put]
//
// Update settings
func (h *ThirdPartyController) put(user models.User, c *fiber.Ctx) error {
if err := h.BodyParserValidator(c, &smsgateway.DeviceSettings{}); err != nil {
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Invalid settings format: %v", err))
}

settings := make(map[string]any, 8)

if err := c.BodyParser(&settings); err != nil {
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Failed to parse request body: %v", err))
}

updated, err := h.settingsSvc.ReplaceSettings(user.ID, settings)

if err != nil {
return fmt.Errorf("can't update settings: %w", err)
}

return c.JSON(updated)
}

// @Summary Partially update settings
// @Description Partially updates settings for a specific user
// @Security ApiAuth
// @Tags User, Settings
// @Accept json
// @Produce json
// @Param request body smsgateway.DeviceSettings true "Settings"
// @Success 200 {object} object "Settings updated"
// @Failure 400 {object} smsgateway.ErrorResponse "Invalid request"
// @Failure 401 {object} smsgateway.ErrorResponse "Unauthorized"
// @Failure 500 {object} smsgateway.ErrorResponse "Internal server error"
// @Router /3rdparty/v1/settings [patch]
//
// Partially update settings
func (h *ThirdPartyController) patch(user models.User, c *fiber.Ctx) error {
if err := h.BodyParserValidator(c, &smsgateway.DeviceSettings{}); err != nil {
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Invalid settings format: %v", err))
}

settings := make(map[string]any, 8)

if err := c.BodyParser(&settings); err != nil {
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Failed to parse request body: %v", err))
}

updated, err := h.settingsSvc.UpdateSettings(user.ID, settings)
if err != nil {
return fmt.Errorf("can't update settings: %w", err)
}

return c.JSON(updated)
}

func (h *ThirdPartyController) Register(app fiber.Router) {
app.Get("", userauth.WithUser(h.get))
app.Patch("", userauth.WithUser(h.patch))
app.Put("", userauth.WithUser(h.put))
}

func NewThirdPartyController(params thirdPartyControllerParams) *ThirdPartyController {
return &ThirdPartyController{
Handler: base.Handler{
Logger: params.Logger.Named("settings"),
Validator: params.Validator,
},
devicesSvc: params.DevicesSvc,
settingsSvc: params.SettingsSvc,
}
}
64 changes: 64 additions & 0 deletions internal/sms-gateway/handlers/settings/mobile.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package settings

import (
"fmt"

"github.com/android-sms-gateway/server/internal/sms-gateway/handlers/base"
"github.com/android-sms-gateway/server/internal/sms-gateway/handlers/middlewares/deviceauth"
"github.com/android-sms-gateway/server/internal/sms-gateway/models"
"github.com/android-sms-gateway/server/internal/sms-gateway/modules/devices"
"github.com/android-sms-gateway/server/internal/sms-gateway/modules/settings"
"github.com/gofiber/fiber/v2"
"go.uber.org/fx"
"go.uber.org/zap"
)

type mobileControllerParams struct {
fx.In

DevicesSvc *devices.Service
SettingsSvc *settings.Service

Logger *zap.Logger
}

type MobileController struct {
base.Handler

devicesSvc *devices.Service
settingsSvc *settings.Service
}

// @Summary Get settings
// @Description Returns settings for a device
// @Security MobileToken
// @Tags Device, Settings
// @Produce json
// @Success 200 {object} smsgateway.DeviceSettings "Settings"
// @Failure 401 {object} smsgateway.ErrorResponse "Unauthorized"
// @Failure 500 {object} smsgateway.ErrorResponse "Internal server error"
// @Router /mobile/v1/settings [get]
//
// Get settings
func (h *MobileController) get(device models.Device, c *fiber.Ctx) error {
settings, err := h.settingsSvc.GetSettings(device.UserID, false)
if err != nil {
return fmt.Errorf("can't get settings for device %s (user ID: %s): %w", device.ID, device.UserID, err)
}

return c.JSON(settings)
}

func (h *MobileController) Register(router fiber.Router) {
router.Get("", deviceauth.WithDevice(h.get))
}

func NewMobileController(params mobileControllerParams) *MobileController {
return &MobileController{
Handler: base.Handler{
Logger: params.Logger.Named("settings"),
},
devicesSvc: params.DevicesSvc,
settingsSvc: params.SettingsSvc,
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
-- +goose Up
-- +goose StatementBegin
CREATE TABLE `device_settings` (
`user_id` varchar(32) NOT NULL,
`settings` json NOT NULL,
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (`user_id`),
CONSTRAINT `fk_device_settings_user` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE
);
-- +goose StatementEnd
---
-- +goose Down
-- +goose StatementBegin
DROP TABLE `device_settings`;
-- +goose StatementEnd
Loading
Loading