[openapi] add OpenAPI documentation hosting options

This commit is contained in:
Aleksandr Soloshenko 2025-08-27 13:51:28 +07:00 committed by Aleksandr
parent 8a7f6be03c
commit abea9b5fdc
27 changed files with 255 additions and 2582 deletions

View File

@ -53,13 +53,6 @@ docker:
docker-dev:
docker compose -f deployments/docker-compose/docker-compose.dev.yml up --build
api-docs:
swag fmt -g ./cmd/$(project_name)/main.go \
&& swag init --outputTypes json,yaml --parseDependency -g ./cmd/$(project_name)/main.go -o ./pkg/swagger/docs
view-docs:
php -S 127.0.0.1:8080 -t ./api
clean:
docker compose -f deployments/docker-compose/docker-compose.yml down --volumes

1
api
View File

@ -1 +0,0 @@
pkg/swagger/docs

View File

@ -5,6 +5,11 @@ http: # http server config
listen: 127.0.0.1:3000 # listen address [HTTP__LISTEN]
proxies:
- "127.0.0.1" # proxy address [HTTP__PROXIES]
api:
host: # public API host [HTTP__API__HOST]
path: /api # public API path [HTTP__API__PATH]
openapi:
enabled: false # openapi enabled [HTTP__OPENAPI__ENABLED]
database: # database
dialect: mysql # database dialect (only mysql supported at the moment) [DATABASE__DIALECT]
host: localhost # database host [DATABASE__HOST]

15
go.mod
View File

@ -14,10 +14,12 @@ require (
github.com/go-playground/validator/v10 v10.16.0
github.com/go-sql-driver/mysql v1.7.1
github.com/gofiber/fiber/v2 v2.52.9
github.com/gofiber/swagger v1.1.1
github.com/google/uuid v1.6.0
github.com/jaevor/go-nanoid v1.3.0
github.com/nyaruka/phonenumbers v1.4.0
github.com/prometheus/client_golang v1.19.1
github.com/swaggo/swag v1.16.6
go.uber.org/fx v1.20.1
go.uber.org/zap v1.26.0
golang.org/x/crypto v0.37.0
@ -33,11 +35,18 @@ require (
cloud.google.com/go/iam v1.1.4 // indirect
cloud.google.com/go/longrunning v0.5.3 // indirect
cloud.google.com/go/storage v1.33.0 // indirect
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/MicahParks/keyfunc v1.9.0 // indirect
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/andybalholm/brotli v1.1.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.19.6 // indirect
github.com/go-openapi/spec v0.20.4 // indirect
github.com/go-openapi/swag v0.19.15 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/gofiber/adaptor/v2 v2.2.1 // indirect
@ -55,9 +64,11 @@ require (
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/joho/godotenv v1.5.1 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/kelseyhightower/envconfig v1.4.0 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/mailru/easyjson v0.7.6 // indirect
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.16 // indirect
@ -70,6 +81,7 @@ require (
github.com/prometheus/procfs v0.15.1 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/sethvargo/go-retry v0.2.4 // indirect
github.com/swaggo/files/v2 v2.0.2 // indirect
github.com/tinylib/msgp v1.2.5 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.55.0 // indirect
@ -78,12 +90,14 @@ require (
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/dig v1.17.1 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/mod v0.18.0 // indirect
golang.org/x/net v0.39.0 // indirect
golang.org/x/oauth2 v0.27.0 // indirect
golang.org/x/sync v0.13.0 // indirect
golang.org/x/sys v0.32.0 // indirect
golang.org/x/text v0.24.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.22.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
google.golang.org/appengine/v2 v2.0.5 // indirect
google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405 // indirect
@ -91,6 +105,7 @@ require (
google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect
google.golang.org/grpc v1.59.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gorm.io/driver/mysql v1.5.2 // indirect
gorm.io/driver/postgres v1.5.6 // indirect

47
go.sum
View File

@ -20,22 +20,18 @@ github.com/ClickHouse/ch-go v0.58.2 h1:jSm2szHbT9MCAB1rJ3WuCJqmGLi5UTjlNu+f530UT
github.com/ClickHouse/ch-go v0.58.2/go.mod h1:Ap/0bEmiLa14gYjCiRkYGbXvbe8vwdrfTYWhsuQ99aw=
github.com/ClickHouse/clickhouse-go/v2 v2.16.0 h1:rhMfnPewXPnY4Q4lQRGdYuTLRBRKJEIEYHtbUMrzmvI=
github.com/ClickHouse/clickhouse-go/v2 v2.16.0/go.mod h1:J7SPfIxwR+x4mQ+o8MLSe0oY50NNntEqCIjFe/T1VPM=
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
github.com/MicahParks/keyfunc v1.9.0 h1:lhKd5xrFHLNOWrDc4Tyb/Q1AJ4LCzQ48GVJyVIID3+o=
github.com/MicahParks/keyfunc v1.9.0/go.mod h1:IdnCilugA0O/99dW+/MkvlyrsX8+L8+x95xuVNtM5jw=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
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.9.1 h1:i9hKf+kgaJo9ykWKoeno3MliOThoIlwO0N2eed6bY+Q=
github.com/android-sms-gateway/client-go v1.9.1/go.mod h1:DQsReciU1xcaVW3T5Z2bqslNdsAwCFCtghawmA6g6L4=
github.com/android-sms-gateway/client-go v1.9.2-0.20250805133721-9ebe535d038a h1:ibYxk2m1Qcwydi/GDHpDnUCcLORqLpYGxm6XModDFnM=
github.com/android-sms-gateway/client-go v1.9.2-0.20250805133721-9ebe535d038a/go.mod h1:DQsReciU1xcaVW3T5Z2bqslNdsAwCFCtghawmA6g6L4=
github.com/android-sms-gateway/client-go v1.9.2-0.20250805235002-a840b364bc1d h1:gurnY2hszJ1Yn8vt81Qn9yF/8zcmJK4YzhZAgsYUt/4=
github.com/android-sms-gateway/client-go v1.9.2-0.20250805235002-a840b364bc1d/go.mod h1:DQsReciU1xcaVW3T5Z2bqslNdsAwCFCtghawmA6g6L4=
github.com/android-sms-gateway/client-go v1.9.2 h1:e9HFgvR+LRMV0dOJvFkxt998UxOMWNf8hfnXwMIc39I=
github.com/android-sms-gateway/client-go v1.9.2/go.mod h1:DQsReciU1xcaVW3T5Z2bqslNdsAwCFCtghawmA6g6L4=
github.com/android-sms-gateway/client-go v1.9.3-0.20250812120334-98abf615cd72 h1:GNITeDA5klQSOfV3qYMybuaBCaGoI7I8vDSY3VuXBFE=
github.com/android-sms-gateway/client-go v1.9.3-0.20250812120334-98abf615cd72/go.mod h1:DQsReciU1xcaVW3T5Z2bqslNdsAwCFCtghawmA6g6L4=
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/android-sms-gateway/client-go v1.9.3 h1:Ur2zS5P76UUTQVKJVqPfTdvY7oYN/JBLh8ZEGViRL3o=
github.com/android-sms-gateway/client-go v1.9.3/go.mod h1:DQsReciU1xcaVW3T5Z2bqslNdsAwCFCtghawmA6g6L4=
github.com/android-sms-gateway/client-go v1.9.5-0.20250823005321-9f448350966b h1:50u5sKrJlT4Ah2Ma0NqO+hsNmHwCOZyn0LpUzVfomaE=
@ -64,6 +60,7 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/containerd/continuity v0.4.3 h1:6HVkalIp+2u1ZLH1J/pYX2oBVXlJZvh1X1A7bEZ9Su8=
github.com/containerd/continuity v0.4.3/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -91,6 +88,16 @@ github.com/go-faster/city v1.0.1 h1:4WAxSZ3V2Ws4QRDrscLEDcibJY8uf41H6AhXDrNDcGw=
github.com/go-faster/city v1.0.1/go.mod h1:jKcUJId49qdW3L1qKHH/3wPeUstCVpVSXTM6vO3VcTw=
github.com/go-faster/errors v0.6.1 h1:nNIPOBkprlKzkThvS/0YaX8Zs9KewLCOSFQS5BU06FI=
github.com/go-faster/errors v0.6.1/go.mod h1:5MGV2/2T9yvlrbhe9pD9LO5Z/2zCSq2T8j+Jpi2LAyY=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs=
github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns=
github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M=
github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM=
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
@ -108,6 +115,8 @@ github.com/gofiber/contrib/fiberzap/v2 v2.1.2 h1:7Z1BqS1sYK9e9jTwqPcWx9qQt46PI8o
github.com/gofiber/contrib/fiberzap/v2 v2.1.2/go.mod h1:ulCCQOdDYABGsOQfbndASmCsCN86hsC96iKoOTNYfy8=
github.com/gofiber/fiber/v2 v2.52.9 h1:YjKl5DOiyP3j0mO61u3NTmK7or8GzzWzCFzkboyP5cw=
github.com/gofiber/fiber/v2 v2.52.9/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
github.com/gofiber/swagger v1.1.1 h1:FZVhVQQ9s1ZKLHL/O0loLh49bYB5l1HEAgxDlcTtkRA=
github.com/gofiber/swagger v1.1.1/go.mod h1:vtvY/sQAMc/lGTUCg0lqmBL7Ht9O7uzChpbvJeJQINw=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
@ -175,6 +184,8 @@ github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4=
github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
@ -190,6 +201,10 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
@ -205,6 +220,7 @@ github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nyaruka/phonenumbers v1.4.0 h1:ddhWiHnHCIX3n6ETDA58Zq5dkxkjlvgrDWM2OHHPCzU=
github.com/nyaruka/phonenumbers v1.4.0/go.mod h1:gv+CtldaFz+G3vHHnasBSirAi3O2XLqZzVWz4V1pl2E=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
@ -256,6 +272,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
@ -263,6 +280,10 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/swaggo/files/v2 v2.0.2 h1:Bq4tgS/yxLB/3nwOMcul5oLEUKa877Ykgz3CJMVbQKU=
github.com/swaggo/files/v2 v2.0.2/go.mod h1:TVqetIzZsO9OhHX1Am9sRf9LdrFZqoK49N37KON/jr0=
github.com/swaggo/swag v1.16.6 h1:qBNcx53ZaX+M5dxVyTrgQ0PJ/ACK+NzhwcbieTt+9yI=
github.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg=
github.com/tinylib/msgp v1.2.5 h1:WeQg1whrXRFiZusidTQqzETkRpGjFjcIhW6uqWH09po=
github.com/tinylib/msgp v1.2.5/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
@ -334,6 +355,7 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
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-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
@ -353,6 +375,7 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -365,6 +388,7 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
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.6/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.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
@ -425,12 +449,15 @@ google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6h
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -24,6 +24,18 @@ type Gateway struct {
type HTTP struct {
Listen string `yaml:"listen" envconfig:"HTTP__LISTEN"` // listen address
Proxies []string `yaml:"proxies" envconfig:"HTTP__PROXIES"` // proxies
API API `yaml:"api"`
OpenAPI OpenAPI `yaml:"openapi"`
}
type API struct {
Host string `yaml:"host" envconfig:"HTTP__API__HOST"` // public API host
Path string `yaml:"path" envconfig:"HTTP__API__PATH"` // public API path
}
type OpenAPI struct {
Enabled bool `yaml:"enabled" envconfig:"HTTP__OPENAPI__ENABLED"` // openapi enabled
}
type Database struct {

View File

@ -1,6 +1,7 @@
package config
import (
"strings"
"time"
"github.com/android-sms-gateway/server/internal/sms-gateway/handlers"
@ -53,7 +54,7 @@ var Module = fx.Module(
}),
fx.Provide(func(cfg Config) push.Config {
mode := push.ModeFCM
if cfg.Gateway.Mode == "private" {
if cfg.Gateway.Mode == GatewayModePrivate {
mode = push.ModeUpstream
}
@ -78,8 +79,25 @@ var Module = fx.Module(
}
}),
fx.Provide(func(cfg Config) handlers.Config {
// Default and normalize API path/host
if cfg.HTTP.API.Host == "" {
cfg.HTTP.API.Path = "/api"
}
// Ensure leading slash and trim trailing slash (except root)
if !strings.HasPrefix(cfg.HTTP.API.Path, "/") {
cfg.HTTP.API.Path = "/" + cfg.HTTP.API.Path
}
if cfg.HTTP.API.Path != "/" && strings.HasSuffix(cfg.HTTP.API.Path, "/") {
cfg.HTTP.API.Path = strings.TrimRight(cfg.HTTP.API.Path, "/")
}
// Guard against misconfigured scheme in host (accept "host[:port]" only)
cfg.HTTP.API.Host = strings.TrimPrefix(strings.TrimPrefix(cfg.HTTP.API.Host, "https://"), "http://")
return handlers.Config{
GatewayMode: handlers.GatewayMode(cfg.Gateway.Mode),
PublicHost: cfg.HTTP.API.Host,
PublicPath: cfg.HTTP.API.Path,
UpstreamEnabled: cfg.Gateway.Mode == GatewayModePublic,
OpenAPIEnabled: cfg.HTTP.OpenAPI.Enabled,
}
}),
fx.Provide(func(cfg Config) messages.Config {

View File

@ -18,6 +18,7 @@ import (
"github.com/android-sms-gateway/server/internal/sms-gateway/modules/settings"
"github.com/android-sms-gateway/server/internal/sms-gateway/modules/sse"
"github.com/android-sms-gateway/server/internal/sms-gateway/modules/webhooks"
"github.com/android-sms-gateway/server/internal/sms-gateway/openapi"
"github.com/capcom6/go-infra-fx/cli"
"github.com/capcom6/go-infra-fx/db"
"github.com/capcom6/go-infra-fx/http"
@ -36,6 +37,7 @@ var Module = fx.Module(
appdb.Module,
http.Module,
validator.Module,
openapi.Module(),
handlers.Module,
auth.Module,
push.Module,

View File

@ -1,12 +1,11 @@
package handlers
type GatewayMode string
const (
GatewayModePrivate GatewayMode = "private"
GatewayModePublic GatewayMode = "public"
)
type Config struct {
GatewayMode GatewayMode
// PublicHost is host[:port] without scheme. Empty → use request Host.
PublicHost string
// PublicPath is API base path; normalized to start with "/" and have no trailing "/".
PublicPath string
UpstreamEnabled bool
OpenAPIEnabled bool
}

View File

@ -1,39 +1,59 @@
package handlers
import (
"net/http"
"path"
"strings"
"github.com/android-sms-gateway/server/pkg/swagger"
"github.com/android-sms-gateway/server/internal/sms-gateway/openapi"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/filesystem"
)
type rootHandler struct {
healthHandler *healthHandler
config Config
healthHandler *healthHandler
openapiHandler *openapi.Handler
}
func (h *rootHandler) Register(app *fiber.App) {
app.Use(func(c *fiber.Ctx) error {
if c.Path() == "/api" {
return c.Redirect("/api/", fiber.StatusMovedPermanently)
if h.config.PublicPath != "/api" {
app.Use(func(c *fiber.Ctx) error {
err := c.Next()
location := c.GetRespHeader(fiber.HeaderLocation)
if after, ok := strings.CutPrefix(location, "/api"); ok {
c.Set(fiber.HeaderLocation, path.Join(h.config.PublicPath, after))
}
return err
})
}
h.healthHandler.Register(app)
h.registerOpenAPI(app)
}
func (h *rootHandler) registerOpenAPI(router fiber.Router) {
if !h.config.OpenAPIEnabled {
return
}
router.Use(func(c *fiber.Ctx) error {
if c.Path() == "/api" || c.Path() == "/api/" {
return c.Redirect("/api/docs", fiber.StatusMovedPermanently)
}
return c.Next()
})
h.healthHandler.Register(app)
app.Use("/api", filesystem.New(filesystem.Config{
Root: http.FS(swagger.Docs),
PathPrefix: "docs",
MaxAge: 1 * 24 * 60 * 60,
}), func(c *fiber.Ctx) error {
// The filesystem middleware set 404 status before next, so we need to override it
return c.Status(fiber.StatusOK).Next()
})
h.openapiHandler.Register(router.Group("/api/docs"), h.config.PublicHost, h.config.PublicPath)
}
func newRootHandler(healthHandler *healthHandler) *rootHandler {
func newRootHandler(cfg Config, healthHandler *healthHandler, openapiHandler *openapi.Handler) *rootHandler {
return &rootHandler{
healthHandler: healthHandler,
config: cfg,
healthHandler: healthHandler,
openapiHandler: openapiHandler,
}
}

View File

@ -81,9 +81,14 @@ func (h *upstreamHandler) postPush(c *fiber.Ctx) error {
return c.SendStatus(fiber.StatusAccepted)
}
// Register registers upstream handlers with the given router.
//
// If upstream is disabled in the configuration, this function does nothing.
//
// It registers a single handler for sending push notifications to
// /upstream/v1/push, with a rate limiter of 5 requests per minute.
func (h *upstreamHandler) Register(router fiber.Router) {
// register only in public mode
if h.config.GatewayMode != GatewayModePublic {
if !h.config.UpstreamEnabled {
return
}

View File

@ -1,18 +1,22 @@
{
"schemes": [
"https"
],
// Package openapi Code generated by swaggo/swag. DO NOT EDIT
package openapi
import "github.com/swaggo/swag"
const docTemplate = `{
"schemes": {{ marshal .Schemes }},
"swagger": "2.0",
"info": {
"description": "This API provides programmatic access to sending SMS messages on Android devices. Features include sending SMS, checking message status, device management, webhook configuration, and system health checks.",
"title": "SMS Gateway for Android™ API",
"description": "{{escape .Description}}",
"title": "{{.Title}}",
"contact": {
"name": "SMSGate Support",
"email": "support@sms-gate.app"
},
"version": "{APP_VERSION}"
"version": "{{.Version}}"
},
"host": "api.sms-gate.app",
"host": "{{.Host}}",
"basePath": "{{.BasePath}}",
"paths": {
"/3rdparty/v1/devices": {
"get": {
@ -150,7 +154,7 @@
"ApiAuth": []
}
],
"description": "Initiates process of inbox messages export via webhooks. For each message the `sms:received` webhook will be triggered. The webhooks will be triggered without specific order.",
"description": "Initiates process of inbox messages export via webhooks. For each message the ` + "`" + `sms:received` + "`" + ` webhook will be triggered. The webhooks will be triggered without specific order.",
"consumes": [
"application/json"
],
@ -358,7 +362,7 @@
"ApiAuth": []
}
],
"description": "Enqueues a message for sending. If `deviceId` is set, the specified device is used; otherwise a random registered device is chosen.",
"description": "Enqueues a message for sending. If ` + "`" + `deviceId` + "`" + ` is set, the specified device is used; otherwise a random registered device is chosen.",
"consumes": [
"application/json"
],
@ -781,513 +785,6 @@
}
}
}
},
"/mobile/v1/device": {
"get": {
"description": "Returns device information",
"produces": [
"application/json"
],
"tags": [
"Device"
],
"summary": "Get device information",
"responses": {
"200": {
"description": "Device information",
"schema": {
"$ref": "#/definitions/smsgateway.MobileDeviceResponse"
}
},
"500": {
"description": "Internal server error",
"schema": {
"$ref": "#/definitions/smsgateway.ErrorResponse"
}
}
}
},
"post": {
"security": [
{
"ApiAuth": []
},
{
"UserCode": []
},
{
"ServerKey": []
}
],
"description": "Registers new device for new or existing user. Returns user credentials only for new users",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Device"
],
"summary": "Register device",
"parameters": [
{
"description": "Device registration request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/smsgateway.MobileRegisterRequest"
}
}
],
"responses": {
"201": {
"description": "Device registered",
"schema": {
"$ref": "#/definitions/smsgateway.MobileRegisterResponse"
}
},
"400": {
"description": "Invalid request",
"schema": {
"$ref": "#/definitions/smsgateway.ErrorResponse"
}
},
"401": {
"description": "Unauthorized (private mode only)",
"schema": {
"$ref": "#/definitions/smsgateway.ErrorResponse"
}
},
"429": {
"description": "Too many requests",
"schema": {
"$ref": "#/definitions/smsgateway.ErrorResponse"
}
},
"500": {
"description": "Internal server error",
"schema": {
"$ref": "#/definitions/smsgateway.ErrorResponse"
}
}
}
},
"patch": {
"security": [
{
"MobileToken": []
}
],
"description": "Updates push token for device",
"consumes": [
"application/json"
],
"tags": [
"Device"
],
"summary": "Update device",
"parameters": [
{
"description": "Device update request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/smsgateway.MobileUpdateRequest"
}
}
],
"responses": {
"204": {
"description": "Successfully updated"
},
"400": {
"description": "Invalid request",
"schema": {
"$ref": "#/definitions/smsgateway.ErrorResponse"
}
},
"403": {
"description": "Forbidden (wrong device ID)",
"schema": {
"$ref": "#/definitions/smsgateway.ErrorResponse"
}
},
"500": {
"description": "Internal server error",
"schema": {
"$ref": "#/definitions/smsgateway.ErrorResponse"
}
}
}
}
},
"/mobile/v1/events": {
"get": {
"security": [
{
"MobileToken": []
}
],
"description": "Returns events stream for a device",
"produces": [
"text/event-stream"
],
"tags": [
"Device",
"Events"
],
"summary": "Get events",
"responses": {
"200": {
"description": "Event",
"schema": {
"type": "string"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/smsgateway.ErrorResponse"
}
},
"500": {
"description": "Internal server error",
"schema": {
"$ref": "#/definitions/smsgateway.ErrorResponse"
}
}
},
"x-sse": true
}
},
"/mobile/v1/message": {
"get": {
"security": [
{
"MobileToken": []
}
],
"description": "Returns list of pending messages",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Device",
"Messages"
],
"summary": "Get messages for sending",
"parameters": [
{
"enum": [
"lifo",
"fifo"
],
"type": "string",
"default": "lifo",
"description": "Message processing order: lifo (default) or fifo",
"name": "order",
"in": "query"
}
],
"responses": {
"200": {
"description": "List of pending messages",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/smsgateway.MobileMessage"
}
}
},
"400": {
"description": "Invalid request",
"schema": {
"$ref": "#/definitions/smsgateway.ErrorResponse"
}
},
"500": {
"description": "Internal server error",
"schema": {
"$ref": "#/definitions/smsgateway.ErrorResponse"
}
}
}
},
"patch": {
"security": [
{
"MobileToken": []
}
],
"description": "Updates message state",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Device",
"Messages"
],
"summary": "Update message state",
"parameters": [
{
"description": "List of message state updates",
"name": "request",
"in": "body",
"required": true,
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/smsgateway.MobilePatchMessageItem"
}
}
}
],
"responses": {
"204": {
"description": "Successfully updated"
},
"400": {
"description": "Invalid request",
"schema": {
"$ref": "#/definitions/smsgateway.ErrorResponse"
}
},
"500": {
"description": "Internal server error",
"schema": {
"$ref": "#/definitions/smsgateway.ErrorResponse"
}
}
}
}
},
"/mobile/v1/settings": {
"get": {
"security": [
{
"MobileToken": []
}
],
"description": "Returns settings for a device",
"produces": [
"application/json"
],
"tags": [
"Device",
"Settings"
],
"summary": "Get settings",
"responses": {
"200": {
"description": "Settings",
"schema": {
"$ref": "#/definitions/smsgateway.DeviceSettings"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/smsgateway.ErrorResponse"
}
},
"500": {
"description": "Internal server error",
"schema": {
"$ref": "#/definitions/smsgateway.ErrorResponse"
}
}
}
}
},
"/mobile/v1/user/code": {
"get": {
"security": [
{
"ApiAuth": []
}
],
"description": "Returns one-time code for device registration",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Device"
],
"summary": "Get one-time code for device registration",
"responses": {
"200": {
"description": "User code",
"schema": {
"$ref": "#/definitions/smsgateway.MobileUserCodeResponse"
}
},
"500": {
"description": "Internal server error",
"schema": {
"$ref": "#/definitions/smsgateway.ErrorResponse"
}
}
}
}
},
"/mobile/v1/user/password": {
"patch": {
"security": [
{
"MobileToken": []
}
],
"description": "Changes the user's password",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Device"
],
"summary": "Change password",
"parameters": [
{
"description": "Password change request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/smsgateway.MobileChangePasswordRequest"
}
}
],
"responses": {
"204": {
"description": "Password changed successfully"
},
"400": {
"description": "Invalid request",
"schema": {
"$ref": "#/definitions/smsgateway.ErrorResponse"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/smsgateway.ErrorResponse"
}
},
"500": {
"description": "Internal server error",
"schema": {
"$ref": "#/definitions/smsgateway.ErrorResponse"
}
}
}
}
},
"/mobile/v1/webhooks": {
"get": {
"security": [
{
"MobileToken": []
}
],
"description": "Returns list of registered webhooks for device",
"produces": [
"application/json"
],
"tags": [
"Device",
"Webhooks"
],
"summary": "List webhooks",
"responses": {
"200": {
"description": "Webhook list",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/smsgateway.Webhook"
}
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/smsgateway.ErrorResponse"
}
},
"500": {
"description": "Internal server error",
"schema": {
"$ref": "#/definitions/smsgateway.ErrorResponse"
}
}
}
}
},
"/upstream/v1/push": {
"post": {
"description": "Enqueues notifications for sending to devices",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Upstream"
],
"summary": "Send push notifications",
"parameters": [
{
"description": "Push request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/smsgateway.PushNotification"
}
}
}
],
"responses": {
"202": {
"description": "Notification enqueued"
},
"400": {
"description": "Invalid request",
"schema": {
"$ref": "#/definitions/smsgateway.ErrorResponse"
}
},
"429": {
"description": "Too many requests",
"schema": {
"$ref": "#/definitions/smsgateway.ErrorResponse"
}
},
"500": {
"description": "Internal server error",
"schema": {
"$ref": "#/definitions/smsgateway.ErrorResponse"
}
}
}
}
}
},
"definitions": {
@ -1658,7 +1155,7 @@
]
},
"priority": {
"description": "Priority, messages with values greater than `99` will bypass limits and delays",
"description": "Priority, messages with values greater than ` + "`" + `99` + "`" + ` will bypass limits and delays",
"default": 0,
"maximum": 127,
"minimum": -128,
@ -1684,13 +1181,13 @@
]
},
"ttl": {
"description": "Time to live in seconds (conflicts with `validUntil`)",
"description": "Time to live in seconds (conflicts with ` + "`" + `validUntil` + "`" + `)",
"type": "integer",
"minimum": 5,
"example": 86400
},
"validUntil": {
"description": "Valid until (conflicts with `ttl`)",
"description": "Valid until (conflicts with ` + "`" + `ttl` + "`" + `)",
"type": "string",
"example": "2020-01-01T00:00:00Z"
},
@ -1802,253 +1299,16 @@
}
}
},
"smsgateway.MobileChangePasswordRequest": {
"type": "object",
"required": [
"currentPassword",
"newPassword"
"smsgateway.MessagesProcessingOrder": {
"type": "string",
"enum": [
"LIFO",
"FIFO"
],
"properties": {
"currentPassword": {
"description": "Current password",
"type": "string",
"example": "cp2pydvxd2zwpx"
},
"newPassword": {
"description": "New password, at least 14 characters",
"type": "string",
"minLength": 14,
"example": "cp2pydvxd2zwpx"
}
}
},
"smsgateway.MobileDeviceResponse": {
"type": "object",
"properties": {
"device": {
"description": "Device information, empty if device is not registered on the server",
"allOf": [
{
"$ref": "#/definitions/smsgateway.Device"
}
]
},
"externalIp": {
"description": "External IP",
"type": "string"
}
}
},
"smsgateway.MobileMessage": {
"type": "object",
"required": [
"phoneNumbers"
],
"properties": {
"createdAt": {
"description": "Message creation time",
"type": "string",
"example": "2020-01-01T00:00:00Z"
},
"dataMessage": {
"description": "Data message",
"allOf": [
{
"$ref": "#/definitions/smsgateway.DataMessage"
}
]
},
"deviceId": {
"description": "Optional device ID for explicit selection",
"type": "string",
"maxLength": 21,
"example": "PyDmBQZZXYmyxMwED8Fzy"
},
"id": {
"description": "ID (if not set - will be generated)",
"type": "string",
"maxLength": 36,
"example": "PyDmBQZZXYmyxMwED8Fzy"
},
"isEncrypted": {
"description": "Is encrypted",
"type": "boolean",
"example": true
},
"message": {
"description": "Message content\nDeprecated: use TextMessage instead",
"type": "string",
"maxLength": 65535,
"example": "Hello World!"
},
"phoneNumbers": {
"description": "Recipients (phone numbers)",
"type": "array",
"maxItems": 100,
"minItems": 1,
"items": {
"type": "string"
},
"example": [
"79990001234"
]
},
"priority": {
"description": "Priority, messages with values greater than `99` will bypass limits and delays",
"default": 0,
"maximum": 127,
"minimum": -128,
"allOf": [
{
"$ref": "#/definitions/smsgateway.MessagePriority"
}
],
"example": 0
},
"simNumber": {
"description": "SIM card number (1-3), if not set - default SIM will be used",
"type": "integer",
"maximum": 3,
"example": 1
},
"textMessage": {
"description": "Text message",
"allOf": [
{
"$ref": "#/definitions/smsgateway.TextMessage"
}
]
},
"ttl": {
"description": "Time to live in seconds (conflicts with `validUntil`)",
"type": "integer",
"minimum": 5,
"example": 86400
},
"validUntil": {
"description": "Valid until (conflicts with `ttl`)",
"type": "string",
"example": "2020-01-01T00:00:00Z"
},
"withDeliveryReport": {
"description": "With delivery report",
"type": "boolean",
"example": true
}
}
},
"smsgateway.MobilePatchMessageItem": {
"type": "object",
"required": [
"id",
"recipients",
"state"
],
"properties": {
"id": {
"description": "Message ID",
"type": "string",
"maxLength": 36,
"example": "PyDmBQZZXYmyxMwED8Fzy"
},
"recipients": {
"description": "Recipients states",
"type": "array",
"minItems": 1,
"items": {
"$ref": "#/definitions/smsgateway.RecipientState"
}
},
"state": {
"description": "State",
"allOf": [
{
"$ref": "#/definitions/smsgateway.ProcessingState"
}
],
"example": "Pending"
},
"states": {
"description": "History of states",
"type": "object",
"additionalProperties": {
"type": "string"
}
}
}
},
"smsgateway.MobileRegisterRequest": {
"type": "object",
"properties": {
"name": {
"description": "Device name",
"type": "string",
"maxLength": 128,
"example": "Android Phone"
},
"pushToken": {
"description": "FCM token",
"type": "string",
"maxLength": 256,
"example": "gHz-T6NezDlOfllr7F-Be"
}
}
},
"smsgateway.MobileRegisterResponse": {
"type": "object",
"properties": {
"id": {
"description": "New device ID",
"type": "string",
"example": "QslD_GefqiYV6RQXdkM6V"
},
"login": {
"description": "User login",
"type": "string",
"example": "VQ4GII"
},
"password": {
"description": "User password, empty for existing user",
"type": "string",
"example": "cp2pydvxd2zwpx"
},
"token": {
"description": "Device access token",
"type": "string",
"example": "bP0ZdK6rC6hCYZSjzmqhQ"
}
}
},
"smsgateway.MobileUpdateRequest": {
"type": "object",
"properties": {
"id": {
"description": "ID",
"type": "string",
"example": "QslD_GefqiYV6RQXdkM6V"
},
"pushToken": {
"description": "FCM token",
"type": "string",
"maxLength": 256,
"example": "gHz-T6NezDlOfllr7F-Be"
}
}
},
"smsgateway.MobileUserCodeResponse": {
"type": "object",
"properties": {
"code": {
"description": "One-time code",
"type": "string",
"example": "123456"
},
"validUntil": {
"description": "One-time code expiration time",
"type": "string",
"example": "2020-01-01T00:00:00Z"
}
}
"x-enum-varnames": [
"LIFO",
"FIFO"
]
},
"smsgateway.ProcessingState": {
"type": "string",
@ -2074,57 +1334,6 @@
"ProcessingStateFailed"
]
},
"smsgateway.PushEventType": {
"type": "string",
"enum": [
"MessageEnqueued",
"WebhooksUpdated",
"MessagesExportRequested",
"SettingsUpdated"
],
"x-enum-varnames": [
"PushMessageEnqueued",
"PushWebhooksUpdated",
"PushMessagesExportRequested",
"PushSettingsUpdated"
]
},
"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",
"MessagesExportRequested",
"SettingsUpdated"
],
"allOf": [
{
"$ref": "#/definitions/smsgateway.PushEventType"
}
],
"example": "MessageEnqueued"
},
"token": {
"description": "The token of the device that receives the notification.",
"type": "string",
"example": "PyDmBQZZXYmyxMwED8Fzy"
}
}
},
"smsgateway.RecipientState": {
"type": "object",
"required": [
@ -2133,7 +1342,7 @@
],
"properties": {
"error": {
"description": "Error (for `Failed` state)",
"description": "Error (for ` + "`" + `Failed` + "`" + ` state)",
"type": "string",
"example": "timeout"
},
@ -2201,6 +1410,18 @@
"type": "integer",
"minimum": 1
},
"processing_order": {
"description": "MessagesProcessingOrder defines the order in which messages are processed.\nValid values are \"LIFO\" or \"FIFO\".",
"enum": [
"LIFO",
"FIFO"
],
"allOf": [
{
"$ref": "#/definitions/smsgateway.MessagesProcessingOrder"
}
]
},
"send_interval_max": {
"description": "SendIntervalMax is the maximum interval between message sends (in seconds).\nMust be at least 1 when provided and greater than or equal to SendIntervalMin.",
"type": "integer",
@ -2360,4 +1581,22 @@
"in": "header"
}
}
}`
// SwaggerInfo holds exported Swagger Info so clients can modify it
var SwaggerInfo = &swag.Spec{
Version: "{APP_VERSION}",
Host: "api.sms-gate.app",
BasePath: "",
Schemes: []string{"https"},
Title: "SMS Gateway for Android™ API",
Description: "This API provides programmatic access to sending SMS messages on Android devices. Features include sending SMS, checking message status, device management, webhook configuration, and system health checks.",
InfoInstanceName: "swagger",
SwaggerTemplate: docTemplate,
LeftDelim: "{{",
RightDelim: "}}",
}
func init() {
swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
}

View File

@ -0,0 +1,16 @@
package openapi
import (
"go.uber.org/fx"
"go.uber.org/zap"
)
func Module() fx.Option {
return fx.Module(
"openapi",
fx.Decorate(func(log *zap.Logger) *zap.Logger {
return log.Named("openapi")
}),
fx.Provide(New),
)
}

View File

@ -0,0 +1,37 @@
package openapi
import (
"github.com/android-sms-gateway/server/internal/version"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/etag"
"github.com/gofiber/swagger"
)
//go:generate swag init --parseDependency --tags=User,System --outputTypes go -d ../../../ -g ./cmd/sms-gateway/main.go -o ../../../internal/sms-gateway/openapi
type Handler struct {
}
func New() *Handler {
return &Handler{}
}
func (s *Handler) Register(router fiber.Router, publicHost, publicPath string) {
SwaggerInfo.Version = version.AppVersion
SwaggerInfo.Host = publicHost
SwaggerInfo.BasePath = publicPath
router.Use("*",
// Pre-middleware: set host/scheme dynamically
func(c *fiber.Ctx) error {
if SwaggerInfo.Host == "" {
SwaggerInfo.Host = c.Hostname()
}
SwaggerInfo.Schemes = []string{c.Protocol()}
return c.Next()
},
etag.New(etag.Config{Weak: true}),
swagger.New(swagger.Config{Layout: "BaseLayout", URL: "doc.json"}),
)
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 665 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 628 B

View File

@ -1,16 +0,0 @@
html {
box-sizing: border-box;
overflow: -moz-scrollbars-vertical;
overflow-y: scroll;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
body {
margin: 0;
background: #fafafa;
}

View File

@ -1,19 +0,0 @@
<!-- HTML for static distribution bundle build -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Swagger UI</title>
<link rel="stylesheet" type="text/css" href="./swagger-ui.css" />
<link rel="stylesheet" type="text/css" href="index.css" />
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
</head>
<body>
<div id="swagger-ui"></div>
<script src="./swagger-ui-bundle.js" charset="UTF-8"> </script>
<script src="./swagger-ui-standalone-preset.js" charset="UTF-8"> </script>
<script src="./swagger-initializer.js" charset="UTF-8"> </script>
</body>
</html>

View File

@ -1,21 +0,0 @@
window.onload = function () {
//<editor-fold desc="Changeable Configuration Block">
// the following lines will be replaced by docker/configurator, when it runs in a docker-container
window.ui = SwaggerUIBundle({
url: "./swagger.json",
dom_id: '#swagger-ui',
deepLinking: true,
showCommonExtensions: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
],
layout: "BaseLayout",
});
//</editor-fold>
};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +0,0 @@
package swagger
import "embed"
//go:generate swag init --parseDependency --tags=User,System --outputTypes json,yaml -d ../../ -g ./cmd/sms-gateway/main.go -o ../../pkg/swagger/docs
//go:embed docs/*.png docs/*.js docs/*.css docs/swagger.* docs/*.html
var Docs embed.FS