Added: mobile endpoints without patch

This commit is contained in:
Aleksandr Soloshenko 2022-10-06 18:01:21 +07:00
parent 159523154a
commit cad991d442
17 changed files with 510 additions and 19 deletions

View File

@ -4,6 +4,7 @@ import (
"log"
"bitbucket.org/capcom6/smsgatewaybackend/internal/config"
_ "bitbucket.org/capcom6/smsgatewaybackend/internal/smsgateway"
microbase "bitbucket.org/soft-c/gomicrobase"
)

11
go.mod
View File

@ -3,22 +3,24 @@ module bitbucket.org/capcom6/smsgatewaybackend
go 1.18
require (
bitbucket.org/soft-c/gomicrobase v1.1.2-0.20221004142125-98b1dad322bc
bitbucket.org/soft-c/gohelpers v1.0.3-0.20221006072847-aee2524b5192
bitbucket.org/soft-c/gomicrobase v1.1.2-0.20221006080527-7eeddcd13770
github.com/go-playground/validator/v10 v10.11.0
github.com/gofiber/fiber/v2 v2.38.1
github.com/jaevor/go-nanoid v1.3.0
github.com/joho/godotenv v1.4.0
gorm.io/gorm v1.23.8
)
require (
bitbucket.org/soft-c/gohelpers v1.0.2 // indirect
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/ansrivas/fiberprometheus/v2 v2.4.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/go-playground/locales v0.14.0 // indirect
github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/go-playground/validator/v10 v10.11.0 // indirect
github.com/go-sql-driver/mysql v1.6.0 // indirect
github.com/gofiber/adaptor/v2 v2.1.27 // indirect
github.com/gofiber/fiber/v2 v2.38.1 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
@ -38,5 +40,4 @@ require (
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gorm.io/driver/mysql v1.3.6 // indirect
gorm.io/gorm v1.23.8 // indirect
)

22
go.sum
View File

@ -1,9 +1,7 @@
bitbucket.org/soft-c/gohelpers v1.0.2 h1:WP6g2V5jlyoY8ksz0N9VAjcbZeuOAupZSLCEj1kab+s=
bitbucket.org/soft-c/gohelpers v1.0.2/go.mod h1:ltWoO1ZiJoATBjBw6AnV3Qf91qrp2IQCmYZlL/CwKSs=
bitbucket.org/soft-c/gomicrobase v1.1.1 h1:UMgeksfIgR4st2sfITl8GMYwn7HQVoizoygb/yYQNeM=
bitbucket.org/soft-c/gomicrobase v1.1.1/go.mod h1:+Jh2pL0WUtAGJu1twMdQCnxeRXjSbrra4RecZMoe/bw=
bitbucket.org/soft-c/gomicrobase v1.1.2-0.20221004142125-98b1dad322bc h1:Bsq8Uuf6rkg5BYnG9usB0hmCQq5JKAvN2GWW9JhFMUU=
bitbucket.org/soft-c/gomicrobase v1.1.2-0.20221004142125-98b1dad322bc/go.mod h1:Pvv07ulIXfDG5us/ZG5Of7vvjY+tq3fkcrY/MmJUEiE=
bitbucket.org/soft-c/gohelpers v1.0.3-0.20221006072847-aee2524b5192 h1:Mmoyu4csqhWcxCQc0qhSyd9dIt1BCxJ1OYOZA4D/S2U=
bitbucket.org/soft-c/gohelpers v1.0.3-0.20221006072847-aee2524b5192/go.mod h1:AlZzQ1xftfS8rWSwjwTj3/MPMFFfgfNCP3b7xLoEeKM=
bitbucket.org/soft-c/gomicrobase v1.1.2-0.20221006080527-7eeddcd13770 h1:IAEOe5Uv9+zQEaOuT8eHJPUfS64Nc0I9ufq7afFXKUk=
bitbucket.org/soft-c/gomicrobase v1.1.2-0.20221006080527-7eeddcd13770/go.mod h1:Pvv07ulIXfDG5us/ZG5Of7vvjY+tq3fkcrY/MmJUEiE=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
@ -95,8 +93,6 @@ github.com/gofiber/adaptor/v2 v2.1.25/go.mod h1:gOxtwMVqUStB5goAYtKd+hSvGupdd+aR
github.com/gofiber/adaptor/v2 v2.1.27 h1:I3Xjn1qaH6W09shPi8F/kz98AncwbVYWFBV4niBBC0Q=
github.com/gofiber/adaptor/v2 v2.1.27/go.mod h1:xcc+UqppJ+XfYVcqxp2fHyoqKctQxxvZ/IyUybNSIlQ=
github.com/gofiber/fiber/v2 v2.36.0/go.mod h1:tgCr+lierLwLoVHHO/jn3Niannv34WRkQETU8wiL9fQ=
github.com/gofiber/fiber/v2 v2.37.0 h1:KVboSQ7e0wDbSFXNjXKqoigwp9HYUqgWn4uGFaUO1P8=
github.com/gofiber/fiber/v2 v2.37.0/go.mod h1:xm3pDGlfE1xqVKb77iH8weLU0FFoTeWeK3nbiYM2Nh0=
github.com/gofiber/fiber/v2 v2.37.1/go.mod h1:j3UslgQeJQP3mNhBxHnLLE8TPqA1Fd/lrl4gD25rRUY=
github.com/gofiber/fiber/v2 v2.38.1 h1:GEQ/Yt3Wsf2a30iTqtLXlBYJZso0JXPovt/tmj5H9jU=
github.com/gofiber/fiber/v2 v2.38.1/go.mod h1:t0NlbaXzuGH7I+7M4paE848fNWInZ7mfxI/Er1fTth8=
@ -140,6 +136,7 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@ -156,6 +153,8 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/jaevor/go-nanoid v1.3.0 h1:nD+iepesZS6pr3uOVf20vR9GdGgJW1HPaR46gtrxzkg=
github.com/jaevor/go-nanoid v1.3.0/go.mod h1:SI+jFaPuddYkqkVQoNGHs81navCtH388TcrH0RqFKgY=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
@ -174,8 +173,6 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY=
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c=
github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@ -246,12 +243,11 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.38.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I=
github.com/valyala/fasthttp v1.39.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I=
github.com/valyala/fasthttp v1.40.0 h1:CRq/00MfruPGFLTQKY8b+8SfdK60TxNztjRMnH0t1Yc=
github.com/valyala/fasthttp v1.40.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I=
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
@ -391,8 +387,6 @@ golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220907062415-87db552b00fd h1:AZeIEzg+8RCELJYq8w+ODLVxFgLMMigSwO/ffKPEd9U=
golang.org/x/sys v0.0.0-20220907062415-87db552b00fd/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec h1:BkDtF2Ih9xZ7le9ndzTA7KJow28VbQW3odyk/8drmuI=
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=

View File

@ -0,0 +1,12 @@
package smsgateway
import (
"bitbucket.org/capcom6/smsgatewaybackend/internal/smsgateway/handlers"
"bitbucket.org/capcom6/smsgatewaybackend/internal/smsgateway/models"
microbase "bitbucket.org/soft-c/gomicrobase"
)
func init() {
microbase.RegisterMigration(models.Migrate)
microbase.RegisterHandlers(handlers.Register)
}

View File

@ -0,0 +1,29 @@
package handlers
import (
microbase "bitbucket.org/soft-c/gomicrobase"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/basicauth"
)
type thirdPartyHandler struct {
microbase.Handler
}
func (h *thirdPartyHandler) postMessage(c *fiber.Ctx) error {
return fiber.ErrNotImplemented
}
func (h *thirdPartyHandler) register(router fiber.Router) {
router.Use(basicauth.New(basicauth.Config{
Authorizer: func(username string, password string) bool {
return len(username) > 0 && len(password) > 0
},
}))
router.Post("/message", h.postMessage)
}
func newThirdPartyHandler() *thirdPartyHandler {
return &thirdPartyHandler{}
}

View File

@ -0,0 +1,24 @@
package handlers
import (
"bitbucket.org/capcom6/smsgatewaybackend/internal/smsgateway/repositories"
"bitbucket.org/capcom6/smsgatewaybackend/internal/smsgateway/services"
"github.com/go-playground/validator/v10"
"github.com/gofiber/fiber/v2"
"gorm.io/gorm"
)
func Register(router fiber.Router, db *gorm.DB) error {
users := repositories.NewUsersRepository(db)
devices := repositories.NewDevicesRepository(db)
messages := repositories.NewMessagesRepository(db)
validator := validator.New()
authSvc := services.NewAuthService(users, devices)
messagesSvc := services.NewMessagesService(messages)
newMobileHandler(validator, authSvc, messagesSvc).register(router.Group("/mobile/v1"))
newThirdPartyHandler().register(router.Group("/3rdparty/v1"))
return nil
}

View File

@ -0,0 +1,16 @@
package handlers
import (
"fmt"
l "log"
"os"
"reflect"
"strings"
)
type empty struct{}
var packageName = strings.Split(reflect.TypeOf(empty{}).PkgPath(), "/")
var logPrefix = fmt.Sprintf("[%s] ", packageName[len(packageName)-1])
var log = l.New(os.Stdout, logPrefix, l.Ldate|l.Ltime|l.Lshortfile)
var errorLog = l.New(os.Stderr, logPrefix, l.Ldate|l.Ltime|l.Lshortfile)

View File

@ -0,0 +1,113 @@
package handlers
import (
"fmt"
"strings"
"bitbucket.org/capcom6/smsgatewaybackend/internal/smsgateway/models"
"bitbucket.org/capcom6/smsgatewaybackend/internal/smsgateway/services"
"bitbucket.org/capcom6/smsgatewaybackend/pkg/smsgateway"
"bitbucket.org/soft-c/gohelpers/pkg/fiber/middleware/apikey"
microbase "bitbucket.org/soft-c/gomicrobase"
"github.com/go-playground/validator/v10"
"github.com/gofiber/fiber/v2"
"github.com/jaevor/go-nanoid"
)
type mobileHandler struct {
microbase.Handler
authSvc *services.AuthService
messagesSvc *services.MessagesService
idGen func() string
}
func (h *mobileHandler) postRegister(c *fiber.Ctx) error {
req := smsgateway.MobileRegisterRequest{}
if err := h.BodyParserValidator(c, &req); err != nil {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
id := h.idGen()
login := strings.ToUpper(id[:6])
password := strings.ToLower(id[7:])
user, err := h.authSvc.RegisterUser(login, password)
if err != nil {
return fmt.Errorf("can't create user: %w", err)
}
device, err := h.authSvc.RegisterDevice(user.ID, req.Name, req.PushToken)
if err != nil {
return fmt.Errorf("can't register device: %w", err)
}
return c.Status(fiber.StatusCreated).JSON(smsgateway.MobileRegisterResponse{
Id: device.ID,
Token: device.AuthToken,
Login: login,
Password: password,
})
}
func (h *mobileHandler) getMessage(device models.Device, c *fiber.Ctx) error {
messages, err := h.messagesSvc.SelectPending(device.ID)
if err != nil {
return fmt.Errorf("can't get messages: %w", err)
}
return c.JSON(messages)
}
func (h *mobileHandler) patchMessage(device models.Device, c *fiber.Ctx) error {
req := []smsgateway.MessageState{}
if err := c.BodyParser(&req); err != nil {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
if err := h.Validator.Var(req, "required,dive"); err != nil {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
return fiber.ErrNotImplemented
}
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 {
errorLog.Println(err)
return fiber.ErrUnauthorized
}
return handler(device, c)
}
}
func (h *mobileHandler) register(router fiber.Router) {
router.Post("/register", h.postRegister)
router.Use(apikey.New(apikey.Config{
Authorizer: func(token string) bool {
return len(token) > 0
},
}))
router.Get("/message", h.authorize(h.getMessage))
router.Patch("/message", h.authorize(h.patchMessage))
}
func newMobileHandler(validator *validator.Validate, authSvc *services.AuthService, messagesSvc *services.MessagesService) *mobileHandler {
idGen, _ := nanoid.Standard(21)
return &mobileHandler{
Handler: microbase.Handler{Validator: validator},
authSvc: authSvc,
messagesSvc: messagesSvc,
idGen: idGen,
}
}

View File

@ -0,0 +1,62 @@
package models
import (
"time"
"gorm.io/gorm"
)
type MessageState string
const (
MessageStatePending MessageState = "Pending"
MessageStateSent MessageState = "Sent"
MessageStateDelivered MessageState = "Delivered"
MessageStateFailed MessageState = "Failed"
)
type TimedModel struct {
CreatedAt time.Time `gorm:"not null;default:CURRENT_TIMESTAMP(3)"`
UpdatedAt time.Time `gorm:"not null;default:CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"`
DeletedAt gorm.DeletedAt
}
type User struct {
ID string `gorm:"primaryKey;type:varchar(32)"`
PasswordHash string `gorm:"not null;type:varchar(72)"`
Devices []Device `gorm:"foreignKey:UserID;constraint:OnDelete:CASCADE"`
TimedModel
}
type Device struct {
ID string `gorm:"primaryKey;type:char(21)"`
Name string `gorm:"type:varchar(128)"`
AuthToken string `gorm:"not null;uniqueIndex;type:char(21)"`
PushToken string `gorm:"type:varchar(256)"`
UserID string `gorm:"not null;type:varchar(32)"`
TimedModel
}
type Message struct {
ID uint64 `gorm:"primaryKey;type:BIGINT UNSIGNED;autoIncrement"`
DeviceID string `gorm:"not null;type:char(21);uniqueIndex:unq_messages_device_id;index:idx_messages_device_state"`
ExtID string `gorm:"not null;type:varchar(36);uniqueIndex:unq_messages_device_id"`
Message string `gorm:"not null;type:tinytext"`
State MessageState `gorm:"not null;type:enum('Pending','Sent','Delivered','Failed');default:Pending;index:idx_messages_device_state"`
Device Device `gorm:"foreignKey:DeviceID;constraint:OnDelete:CASCADE"`
Recipients []MessageRecipient `gorm:"foreignKey:MessageID;constraint:OnDelete:CASCADE"`
}
type MessageRecipient struct {
MessageID uint64 `gorm:"primaryKey;type:BIGINT UNSIGNED"`
PhoneNumber string `gorm:"primaryKey;type:char(11)"`
State MessageState `gorm:"not null;type:enum('Pending','Sent','Delivered','Failed');default:Pending"`
}
func Migrate(db *gorm.DB) error {
return db.AutoMigrate(&User{}, &Device{}, &Message{}, &MessageRecipient{})
}

View File

@ -0,0 +1,30 @@
package repositories
import (
"bitbucket.org/capcom6/smsgatewaybackend/internal/smsgateway/models"
"gorm.io/gorm"
)
var (
ErrDeviceNotFound = gorm.ErrRecordNotFound
)
type DevicesRepository struct {
db *gorm.DB
}
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 NewDevicesRepository(db *gorm.DB) *DevicesRepository {
return &DevicesRepository{
db: db,
}
}

View File

@ -0,0 +1,27 @@
package repositories
import (
"bitbucket.org/capcom6/smsgatewaybackend/internal/smsgateway/models"
"gorm.io/gorm"
)
type MessagesRepository struct {
db *gorm.DB
}
func (r *MessagesRepository) SelectPending(deviceID string) (messages []models.Message, err error) {
err = r.db.
Where("device_id = ? AND state = ?", deviceID, models.MessageStatePending).
Order("id").
Preload("Recipients").
Find(&messages).
Error
return
}
func NewMessagesRepository(db *gorm.DB) *MessagesRepository {
return &MessagesRepository{
db: db,
}
}

View File

@ -0,0 +1,30 @@
package repositories
import (
"bitbucket.org/capcom6/smsgatewaybackend/internal/smsgateway/models"
"gorm.io/gorm"
)
var (
ErrUserNotFound = gorm.ErrRecordNotFound
)
type UsersRepository struct {
db *gorm.DB
}
func (r *UsersRepository) GetByLogin(login string) (models.User, error) {
user := models.User{}
return user, r.db.Where("id = ?", login).Preload("Devices").Take(&user).Error
}
func (r *UsersRepository) Insert(user *models.User) error {
return r.db.Create(user).Error
}
func NewUsersRepository(db *gorm.DB) *UsersRepository {
return &UsersRepository{
db: db,
}
}

View File

@ -0,0 +1,60 @@
package services
import (
"fmt"
"bitbucket.org/capcom6/smsgatewaybackend/internal/smsgateway/models"
"bitbucket.org/capcom6/smsgatewaybackend/internal/smsgateway/repositories"
"bitbucket.org/soft-c/gohelpers/pkg/crypto"
"github.com/jaevor/go-nanoid"
)
type AuthService struct {
users *repositories.UsersRepository
devices *repositories.DevicesRepository
idgen func() string
}
func (s *AuthService) RegisterUser(login, password string) (models.User, error) {
user := models.User{
ID: login,
}
var err error
if user.PasswordHash, err = crypto.MakeBCryptHash(password); err != nil {
return user, err
}
if err = s.users.Insert(&user); err != nil {
return user, fmt.Errorf("can't create user")
}
return user, nil
}
func (s *AuthService) RegisterDevice(userID, 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)
}
func (s *AuthService) AuthorizeDevice(token string) (models.Device, error) {
return s.devices.GetByToken(token)
}
func NewAuthService(users *repositories.UsersRepository, devices *repositories.DevicesRepository) *AuthService {
idgen, _ := nanoid.Standard(21)
return &AuthService{
users: users,
devices: devices,
idgen: idgen,
}
}

View File

@ -0,0 +1,51 @@
package services
import (
"bitbucket.org/capcom6/smsgatewaybackend/internal/smsgateway/models"
"bitbucket.org/capcom6/smsgatewaybackend/internal/smsgateway/repositories"
"bitbucket.org/capcom6/smsgatewaybackend/pkg/smsgateway"
"github.com/jaevor/go-nanoid"
)
type MessagesService struct {
messages *repositories.MessagesRepository
idgen func() string
}
func (s *MessagesService) SelectPending(deviceID string) ([]smsgateway.Message, error) {
messages, err := s.messages.SelectPending(deviceID)
if err != nil {
return nil, err
}
result := make([]smsgateway.Message, len(messages))
for i, v := range messages {
result[i] = smsgateway.Message{
ID: v.ExtID,
Message: v.Message,
PhoneNumbers: s.recipientsToDomain(v.Recipients),
}
}
return result, nil
}
func (s *MessagesService) recipientsToDomain(input []models.MessageRecipient) []string {
output := make([]string, len(input))
for i, v := range input {
output[i] = v.PhoneNumber
}
return output
}
func NewMessagesService(messages *repositories.MessagesRepository) *MessagesService {
idgen, _ := nanoid.Standard(21)
return &MessagesService{
messages: messages,
idgen: idgen,
}
}

27
pkg/smsgateway/domain.go Normal file
View File

@ -0,0 +1,27 @@
package smsgateway
type ProcessState string
const (
MessageStatePending ProcessState = "Pending"
MessageStateSent ProcessState = "Sent"
MessageStateDelivered ProcessState = "Delivered"
MessageStateFailed ProcessState = "Failed"
)
type Message struct {
ID string `json:"id,omitempty" validate:"omitempty,max=36"`
Message string `json:"message" validate:"required,max=256"`
PhoneNumbers []string `json:"phoneNumbers" validate:"required,min=1,max=100"`
}
type MessageState struct {
ID string `json:"id,omitempty" validate:"omitempty,max=36"`
State ProcessState `json:"state" validate:"required"`
Recipients []RecipientState `json:"recipients" validate:"required,min=1,dive"`
}
type RecipientState struct {
PhoneNumber string `json:"phoneNumber" validate:"required,len=11"`
State ProcessState `json:"state" validate:"required"`
}

View File

@ -0,0 +1,6 @@
package smsgateway
type MobileRegisterRequest struct {
Name string `json:"name,omitempty" validate:"omitempty,max=128"`
PushToken string `json:"pushToken" validate:"omitempty,max=256"`
}

View File

@ -0,0 +1,8 @@
package smsgateway
type MobileRegisterResponse struct {
Id string `json:"id"`
Token string `json:"token"`
Login string `json:"login"`
Password string `json:"password"`
}