Merge pull request #3 from capcom6/testing

TTL, compatibility and logging
This commit is contained in:
capcom6 2023-11-03 08:56:55 +07:00 committed by GitHub
commit 414b7fd26f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 278 additions and 111 deletions

2
.vscode/launch.json vendored
View File

@ -9,7 +9,7 @@
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceRoot}/cmd/smsgateway/main.go",
"program": "${workspaceRoot}/cmd/sms-gateway/main.go",
"cwd": "${workspaceRoot}",
}
]

View File

@ -22,9 +22,21 @@ db-upgrade:
db-upgrade-raw:
go run ./cmd/$(project_name)/main.go db-upgrade
run:
go run cmd/$(project_name)/main.go
test:
go test ./...
go test -cover ./...
build:
go build ./cmd/$(project_name)
install:
go install ./cmd/$(project_name)
docker-dev:
docker-compose -f deployments/docker-compose/docker-compose.dev.yml up --build
api-docs:
swag fmt -g ./cmd/$(project_name)/main.go \
@ -33,4 +45,4 @@ api-docs:
view-docs:
php -S 127.0.0.1:8080 -t ./api
.PHONY: init init-dev air db-upgrade db-upgrade-raw test api-docs view-docs
.PHONY: init init-dev air run test install api-docs docker-dev view-docs

View File

@ -16,16 +16,34 @@ Authorization: Basic IUGFXE:v4ejpbeydvjo1h
{
"message": "Test",
"ttl": {{$randomInt 0 86400}},
"ttl": 60,
"phoneNumbers": [
"79990001234"
]
}
###
GET {{baseUrl}}/api/3rdparty/v1/message/pRl1wf_yez5TikJb_xemU HTTP/1.1
GET {{baseUrl}}/api/3rdparty/v1/message/iZo6T9hw7wTuKLQUIP29I HTTP/1.1
Authorization: Basic IUGFXE:v4ejpbeydvjo1h
###
GET {{baseUrl}}/api/mobile/v1/message HTTP/1.1
Authorization: Bearer KuvE4LBXzvy8QO2ZXDDMP
Authorization: Bearer KuvE4LBXzvy8QO2ZXDDMP
###
PATCH {{baseUrl}}/api/mobile/v1/message HTTP/1.1
Authorization: Bearer KuvE4LBXzvy8QO2ZXDDMP
Content-Type: application/json
[
{
"id": "8GHr-tUz6JBRFpTUXDV4w",
"state": "Pending",
"recipients": [
{
"phoneNumber": "79990001234",
"state": "Pending"
}
]
}
]

View File

@ -0,0 +1,15 @@
FROM golang:1.20-alpine
ENV GO111MODULE="on"
ENV GOOS="linux"
ENV CGO_ENABLED=0
WORKDIR /app
RUN go install github.com/cosmtrek/air@latest && \
go install github.com/go-delve/delve/cmd/dlv@latest
EXPOSE 2345
EXPOSE 3000
CMD ["air", "-c", ".air.toml"]

View File

@ -11,3 +11,8 @@ database:
password: root
database: notifications
debug: true
fcm:
credentials_json: >
{
...
}

View File

@ -1,7 +1,7 @@
-- +goose Up
-- +goose StatementBegin
ALTER TABLE `messages`
ADD `valid_until` datetime;
ADD `valid_until` datetime(3) DEFAULT NULL;
-- +goose StatementEnd
---
-- +goose Down

View File

@ -0,0 +1,23 @@
version: '3'
services:
sms-gateway:
image: sms-gateway
build:
context: .
dockerfile: ../../build/package/Dockerfile.dev
container_name: sms-gateway
environment:
- DEBUG=1
- HTTP__LISTEN=:3000
env_file:
- .env
ports:
- "3000:3000"
- "3456:2345"
volumes:
- "../..:/app"
- "/tmp/sms-gateway/go-cache:/go/pkg"
extra_hosts:
- "host.docker.internal:host-gateway"
restart: 'no'

5
go.mod
View File

@ -5,10 +5,12 @@ go 1.20
require (
firebase.google.com/go/v4 v4.12.1
github.com/go-playground/validator/v10 v10.15.5
github.com/go-sql-driver/mysql v1.7.1
github.com/gofiber/contrib/fiberzap/v2 v2.1.1
github.com/gofiber/fiber/v2 v2.50.0
github.com/jaevor/go-nanoid v1.3.0
github.com/joho/godotenv v1.5.1
github.com/kelseyhightower/envconfig v1.4.0
github.com/nyaruka/phonenumbers v1.1.8
github.com/valyala/fasthttp v1.50.0
go.uber.org/fx v1.20.1
@ -34,7 +36,6 @@ require (
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-sql-driver/mysql v1.7.1 // indirect
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
@ -49,7 +50,9 @@ require (
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/philhofer/fwd v1.1.2 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/tinylib/msgp v1.1.8 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect
go.opencensus.io v0.24.0 // indirect

13
go.sum
View File

@ -96,6 +96,8 @@ github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4=
github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@ -115,6 +117,8 @@ github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZ
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/nyaruka/phonenumbers v1.1.8 h1:mjFu85FeoH2Wy18aOMUvxqi1GgAqiQSJsa/cCC5yu2s=
github.com/nyaruka/phonenumbers v1.1.8/go.mod h1:DC7jZd321FqUe+qWSNcHi10tyIyGNXGcNbfkPvdp1Vs=
github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw=
github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@ -132,6 +136,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0=
github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw=
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.50.0 h1:H7fweIlBm0rXLs2q0XbalvJ6r0CUPFWK3/bB4N13e9M=
@ -171,6 +177,7 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -181,6 +188,7 @@ golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@ -191,6 +199,7 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -204,15 +213,18 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
@ -225,6 +237,7 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@ -16,15 +16,16 @@ type Config struct {
}
type HTTP struct {
Listen string `yaml:"listen"`
Listen string `yaml:"listen" envconfig:"HTTP__LISTEN"`
}
type Database struct {
Host string `yaml:"host"`
Port int `yaml:"port"`
User string `yaml:"user"`
Password string `yaml:"password"`
Database string `yaml:"database"`
Host string `yaml:"host" envconfig:"DATABASE__HOST"`
Port int `yaml:"port" envconfig:"DATABASE__PORT"`
User string `yaml:"user" envconfig:"DATABASE__USER"`
Password string `yaml:"password" envconfig:"DATABASE__PASSWORD"`
Database string `yaml:"database" envconfig:"DATABASE__DATABASE"`
Timezone string `yaml:"timezone" envconfig:"DATABASE__TIMEZONE"`
}
type FCMConfig struct {
@ -41,6 +42,7 @@ var defaultConfig = Config{
User: "sms",
Password: "sms",
Database: "sms",
Timezone: "UTC",
},
FCM: FCMConfig{
CredentialsJSON: "",

View File

@ -1,31 +1,25 @@
package config
import (
"github.com/capcom6/sms-gateway/internal/infra/config"
"github.com/capcom6/sms-gateway/internal/infra/db"
"github.com/capcom6/sms-gateway/internal/infra/http"
"github.com/capcom6/sms-gateway/internal/sms-gateway/services"
"go.uber.org/fx"
"go.uber.org/zap"
)
var Module = fx.Module(
"appconfig",
fx.Provide(
fx.Annotate(
func() any {
return &defaultConfig
},
fx.ResultTags(`name:"config:source"`),
),
func(log *zap.Logger) Config {
if err := config.LoadConfig(&defaultConfig); err != nil {
log.Error("Error loading config", zap.Error(err))
}
return defaultConfig
},
),
fx.Provide(
fx.Annotate(
func(cfg any) Config {
return *cfg.(*Config)
},
fx.ParamTags(`name:"config:result"`),
),
),
// fx.Provide(GetConfig),
fx.Provide(func(cfg Config) http.Config {
return http.Config{
Listen: cfg.HTTP.Listen,
@ -38,6 +32,7 @@ var Module = fx.Module(
User: cfg.Database.User,
Password: cfg.Database.Password,
Database: cfg.Database.Database,
Timezone: cfg.Database.Timezone,
}
}),
fx.Provide(func(cfg Config) services.PushServiceConfig {

View File

@ -5,19 +5,28 @@ import (
"os"
"github.com/joho/godotenv"
"go.uber.org/zap"
"github.com/kelseyhightower/envconfig"
"gopkg.in/yaml.v3"
)
type Config struct {
}
func New(params Param) any {
func LoadConfig(config any) error {
err := godotenv.Load()
if err != nil && !errors.Is(err, os.ErrNotExist) {
params.Logger.Error("Error loading .env file", zap.Error(err))
return err
}
if err := loadFromYaml(config); err != nil {
return err
}
if err := loadFromEnv(config); err != nil {
return err
}
return nil
}
func loadFromYaml(config any) error {
configPath := "config.yml"
if envPath := os.Getenv("CONFIG_PATH"); envPath != "" {
configPath = envPath
@ -25,13 +34,16 @@ func New(params Param) any {
yamlFile, err := os.ReadFile(configPath)
if err != nil {
params.Logger.Error("Error reading config file", zap.Error(err))
return err
}
err = yaml.Unmarshal(yamlFile, params.Config)
if err != nil {
params.Logger.Error("Error unmarshalling config file", zap.Error(err))
if err := yaml.Unmarshal(yamlFile, config); err != nil {
return err
}
return params.Config
return nil
}
func loadFromEnv(config any) error {
return envconfig.Process("", config)
}

View File

@ -1,19 +0,0 @@
package config
import (
"go.uber.org/fx"
"go.uber.org/zap"
)
var Module = fx.Module(
"config",
fx.Decorate(func(log *zap.Logger) *zap.Logger {
return log.Named("config")
}),
fx.Provide(
fx.Annotate(
New,
fx.ResultTags(`name:"config:result"`),
),
),
)

View File

@ -1,13 +0,0 @@
package config
import (
"go.uber.org/fx"
"go.uber.org/zap"
)
type Param struct {
fx.In
Logger *zap.Logger
Config any `name:"config:source"`
}

View File

@ -14,6 +14,7 @@ type Config struct {
User string
Password string
Database string
Timezone string
}
// Helper function to set default values

View File

@ -2,7 +2,10 @@ package db
import (
"fmt"
"log"
"net/url"
sql "github.com/go-sql-driver/mysql"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
@ -13,6 +16,8 @@ func New(params Params) (*gorm.DB, error) {
dsn := makeDSN(params.Config)
cfgGorm := makeConfig(params)
sql.SetLogger(log.Default())
return gorm.Open(mysql.Open(dsn), cfgGorm)
}
@ -27,8 +32,9 @@ func makeConfig(params Params) *gorm.Config {
}
func makeDSN(cfg Config) string {
cfg = configDefault(cfg)
return fmt.Sprintf(
"%s:%s@tcp(%s:%d)/%s?charset=utf8mb4,utf8&parseTime=true&tls=preferred",
cfg.User, cfg.Password, cfg.Host, cfg.Port, cfg.Database,
"%s:%s@tcp(%s:%d)/%s?charset=utf8mb4,utf8&parseTime=true&loc=%s&tls=preferred",
cfg.User, cfg.Password, cfg.Host, cfg.Port, cfg.Database, url.QueryEscape(cfg.Timezone),
)
}

View File

@ -16,3 +16,17 @@ func AsApiHandler(f any) any {
fx.ResultTags(`group:"api-routes"`),
)
}
func errorHandler(c *fiber.Ctx, err error) error {
code := fiber.StatusInternalServerError
// Retrieve the custom status code if it's an fiber.*Error
if e, ok := err.(*fiber.Error); ok {
code = e.Code
}
// Send json error
return c.Status(code).JSON(&fiber.Map{
"message": err.Error(),
})
}

View File

@ -23,6 +23,7 @@ func New(params Params) (*fiber.App, error) {
WriteTimeout: WriteTimeout,
IdleTimeout: IdleTimeout,
DisableStartupMessage: true,
ErrorHandler: errorHandler,
})
app.Use(recover.New())

View File

@ -3,7 +3,6 @@ package smsgateway
import (
appconfig "github.com/capcom6/sms-gateway/internal/config"
"github.com/capcom6/sms-gateway/internal/infra/cli"
"github.com/capcom6/sms-gateway/internal/infra/config"
"github.com/capcom6/sms-gateway/internal/infra/db"
"github.com/capcom6/sms-gateway/internal/infra/http"
"github.com/capcom6/sms-gateway/internal/infra/logger"
@ -22,7 +21,6 @@ var Module = fx.Module(
"server",
cli.Module,
appconfig.Module,
config.Module,
logger.Module,
http.Module,
validator.Module,

View File

@ -1,25 +0,0 @@
package handlers
// func New(params Params) Result {
// return Result{
// Handlers: []http.ApiHanlder{},
// }
// }
// func Register(router fiber.Router, db *gorm.DB) error {
// cfg := config.GetConfig()
// users := repositories.NewUsersRepository(db)
// devices := repositories.NewDevicesRepository(db)
// messages := repositories.NewMessagesRepository(db)
// validator := validator.New()
// authSvc := services.NewAuthService(users, devices)
// pushSvc := services.NewPushService(cfg.FCM.CredentialsJSON)
// messagesSvc := services.NewMessagesService(pushSvc, messages)
// newMobileHandler(validator, authSvc, messagesSvc).register(router.Group("/mobile/v1"))
// newThirdPartyHandler(validator, authSvc, messagesSvc).register(router.Group("/3rdparty/v1"))
// return nil
// }

View File

@ -12,6 +12,7 @@ import (
"github.com/capcom6/sms-gateway/pkg/smsgateway"
"github.com/go-playground/validator/v10"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/limiter"
"github.com/jaevor/go-nanoid"
"go.uber.org/zap"
)
@ -167,7 +168,7 @@ func (h *mobileHandler) authorize(handler func(models.Device, *fiber.Ctx) error)
func (h *mobileHandler) Register(router fiber.Router) {
router = router.Group("/mobile/v1")
router.Post("/device", h.postDevice)
router.Post("/device", limiter.New(), h.postDevice)
router.Use(apikey.New(apikey.Config{
Authorizer: func(token string) bool {

View File

@ -10,7 +10,7 @@ type MessageState string
const (
MessageStatePending MessageState = "Pending"
MessageStateProcessed MessageState = "Processes"
MessageStateProcessed MessageState = "Processed"
MessageStateSent MessageState = "Sent"
MessageStateDelivered MessageState = "Delivered"
MessageStateFailed MessageState = "Failed"
@ -47,7 +47,7 @@ type Message struct {
ExtID string `gorm:"not null;type:varchar(36);uniqueIndex:unq_messages_id_device,priority:1"`
Message string `gorm:"not null;type:tinytext"`
State MessageState `gorm:"not null;type:enum('Pending','Sent','Processed','Delivered','Failed');default:Pending;index:idx_messages_device_state"`
ValidUntil time.Time `gorm:"type:datetime"`
ValidUntil *time.Time `gorm:"type:datetime"`
Device Device `gorm:"foreignKey:DeviceID;constraint:OnDelete:CASCADE"`
Recipients []MessageRecipient `gorm:"foreignKey:MessageID;constraint:OnDelete:CASCADE"`

View File

@ -11,6 +11,7 @@ import (
"github.com/capcom6/sms-gateway/internal/sms-gateway/repositories"
"github.com/capcom6/sms-gateway/pkg/slices"
"github.com/capcom6/sms-gateway/pkg/smsgateway"
"github.com/capcom6/sms-gateway/pkg/types"
"github.com/jaevor/go-nanoid"
"github.com/nyaruka/phonenumbers"
)
@ -45,8 +46,8 @@ func (s *MessagesService) SelectPending(deviceID string) ([]smsgateway.Message,
result := make([]smsgateway.Message, len(messages))
for i, v := range messages {
var ttl *uint64 = nil
if !v.ValidUntil.IsZero() {
delta := time.Until(v.ValidUntil).Seconds()
if v.ValidUntil != nil {
delta := time.Until(*v.ValidUntil).Seconds()
if delta > 0 {
deltaInt := uint64(delta)
ttl = &deltaInt
@ -73,6 +74,10 @@ func (s *MessagesService) UpdateState(deviceID string, message smsgateway.Messag
return err
}
if message.State == smsgateway.MessageStatePending {
message.State = smsgateway.MessageStateProcessed
}
existing.State = models.MessageState(message.State)
existing.Recipients = s.recipientsStateToModel(message.Recipients)
@ -113,9 +118,9 @@ func (s *MessagesService) Enqeue(device models.Device, message smsgateway.Messag
}
}
validUntil := time.Time{}
var validUntil *time.Time = nil
if message.TTL != nil && *message.TTL > 0 {
validUntil = time.Now().Add(time.Duration(*message.TTL) * time.Second)
validUntil = types.AsPointer(time.Now().Add(time.Duration(*message.TTL) * time.Second))
}
msg := models.Message{
@ -153,11 +158,11 @@ func (s *MessagesService) Enqeue(device models.Device, message smsgateway.Messag
func (s *MessagesService) filterTimeouted(messages []models.Message) []models.Message {
result := make([]models.Message, 0, len(messages))
for _, v := range messages {
if v.ValidUntil.IsZero() || time.Now().Before(v.ValidUntil) {
if v.ValidUntil == nil || time.Now().Before(*v.ValidUntil) {
result = append(result, v)
} else if v.State == models.MessageStatePending {
v.State = models.MessageStateFailed
for i, _ := range v.Recipients {
for i := range v.Recipients {
v.Recipients[i].State = models.MessageStateFailed
}
s.Messages.UpdateState(&v)
@ -192,8 +197,17 @@ func (s *MessagesService) recipientsStateToModel(input []smsgateway.RecipientSta
output := make([]models.MessageRecipient, len(input))
for i, v := range input {
phoneNumber := v.PhoneNumber
if len(phoneNumber) > 0 && phoneNumber[0] != '+' {
phoneNumber = "+" + phoneNumber
}
if v.State == smsgateway.MessageStatePending {
v.State = smsgateway.MessageStateProcessed
}
output[i] = models.MessageRecipient{
PhoneNumber: v.PhoneNumber,
PhoneNumber: phoneNumber,
State: models.MessageState(v.State),
}
}

View File

@ -0,0 +1,86 @@
package services
import (
"reflect"
"testing"
"github.com/capcom6/sms-gateway/internal/sms-gateway/models"
"github.com/capcom6/sms-gateway/pkg/smsgateway"
)
func TestMessagesService_recipientsStateToModel(t *testing.T) {
type args struct {
input []smsgateway.RecipientState
}
tests := []struct {
name string
s *MessagesService
args args
want []models.MessageRecipient
}{
{
name: "Without +",
s: &MessagesService{},
args: args{
input: []smsgateway.RecipientState{
{
PhoneNumber: "79990001234",
State: "",
},
},
},
want: []models.MessageRecipient{
{
MessageID: 0,
PhoneNumber: "+79990001234",
State: "",
},
},
},
{
name: "With +",
s: &MessagesService{},
args: args{
input: []smsgateway.RecipientState{
{
PhoneNumber: "+79990001234",
State: "",
},
},
},
want: []models.MessageRecipient{
{
MessageID: 0,
PhoneNumber: "+79990001234",
State: "",
},
},
},
{
name: "Empty phone",
s: &MessagesService{},
args: args{
input: []smsgateway.RecipientState{
{
PhoneNumber: "",
State: "",
},
},
},
want: []models.MessageRecipient{
{
MessageID: 0,
PhoneNumber: "",
State: "",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.s.recipientsStateToModel(tt.args.input); !reflect.DeepEqual(got, tt.want) {
t.Errorf("MessagesService.recipientsStateToModel() = %v, want %v", got, tt.want)
}
})
}
}

5
pkg/types/types.go Normal file
View File

@ -0,0 +1,5 @@
package types
func AsPointer[T any](v T) *T {
return &v
}