Compare commits
3 Commits
fbb670b96b
...
2a5b90b530
Author | SHA1 | Date | |
---|---|---|---|
2a5b90b530 | |||
fe2e682267 | |||
481404c4a2 |
23
go.mod
23
go.mod
@ -6,8 +6,9 @@ require (
|
|||||||
github.com/fatih/color v1.18.0
|
github.com/fatih/color v1.18.0
|
||||||
github.com/go-playground/validator/v10 v10.22.1
|
github.com/go-playground/validator/v10 v10.22.1
|
||||||
github.com/goccy/go-json v0.10.3
|
github.com/goccy/go-json v0.10.3
|
||||||
|
github.com/gofiber/contrib/fiberzerolog v1.0.2
|
||||||
github.com/gofiber/contrib/websocket v1.3.2
|
github.com/gofiber/contrib/websocket v1.3.2
|
||||||
github.com/gofiber/fiber/v2 v2.52.5
|
github.com/gofiber/fiber/v2 v2.52.6
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.1
|
github.com/golang-jwt/jwt/v5 v5.2.1
|
||||||
github.com/json-iterator/go v1.1.12
|
github.com/json-iterator/go v1.1.12
|
||||||
github.com/lib/pq v1.10.9
|
github.com/lib/pq v1.10.9
|
||||||
@ -17,9 +18,9 @@ require (
|
|||||||
github.com/rs/zerolog v1.33.0
|
github.com/rs/zerolog v1.33.0
|
||||||
github.com/samber/lo v1.47.0
|
github.com/samber/lo v1.47.0
|
||||||
github.com/spf13/viper v1.19.0
|
github.com/spf13/viper v1.19.0
|
||||||
github.com/valyala/fasthttp v1.57.0
|
github.com/valyala/fasthttp v1.59.0
|
||||||
go.etcd.io/etcd/client/v3 v3.5.16
|
go.etcd.io/etcd/client/v3 v3.5.16
|
||||||
golang.org/x/text v0.21.0
|
golang.org/x/text v0.22.0
|
||||||
google.golang.org/grpc v1.67.1
|
google.golang.org/grpc v1.67.1
|
||||||
google.golang.org/protobuf v1.35.1
|
google.golang.org/protobuf v1.35.1
|
||||||
gorm.io/datatypes v1.2.4
|
gorm.io/datatypes v1.2.4
|
||||||
@ -48,10 +49,10 @@ require (
|
|||||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
github.com/jinzhu/now v1.1.5 // indirect
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
github.com/klauspost/compress v1.17.11 // indirect
|
github.com/klauspost/compress v1.18.0 // indirect
|
||||||
github.com/leodido/go-urn v1.4.0 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/magiconair/properties v1.8.7 // indirect
|
github.com/magiconair/properties v1.8.7 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
@ -62,6 +63,7 @@ require (
|
|||||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||||
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c // indirect
|
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c // indirect
|
||||||
github.com/rivo/uniseg v0.4.7 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
|
github.com/rogpeppe/go-internal v1.11.0 // indirect
|
||||||
github.com/sagikazarmark/locafero v0.6.0 // indirect
|
github.com/sagikazarmark/locafero v0.6.0 // indirect
|
||||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||||
github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38 // indirect
|
github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38 // indirect
|
||||||
@ -70,18 +72,17 @@ require (
|
|||||||
github.com/spf13/cast v1.7.0 // indirect
|
github.com/spf13/cast v1.7.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/subosito/gotenv v1.6.0 // indirect
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
github.com/tinylib/msgp v1.2.4 // indirect
|
github.com/tinylib/msgp v1.2.5 // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/valyala/tcplisten v1.0.0 // indirect
|
|
||||||
go.etcd.io/etcd/api/v3 v3.5.16 // indirect
|
go.etcd.io/etcd/api/v3 v3.5.16 // indirect
|
||||||
go.etcd.io/etcd/client/pkg/v3 v3.5.16 // indirect
|
go.etcd.io/etcd/client/pkg/v3 v3.5.16 // indirect
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.uber.org/zap v1.27.0 // indirect
|
go.uber.org/zap v1.27.0 // indirect
|
||||||
golang.org/x/crypto v0.28.0 // indirect
|
golang.org/x/crypto v0.33.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
|
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
|
||||||
golang.org/x/net v0.30.0 // indirect
|
golang.org/x/net v0.35.0 // indirect
|
||||||
golang.org/x/sync v0.10.0 // indirect
|
golang.org/x/sync v0.11.0 // indirect
|
||||||
golang.org/x/sys v0.26.0 // indirect
|
golang.org/x/sys v0.30.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20241021214115-324edc3d5d38 // indirect
|
google.golang.org/genproto/googleapis/api v0.0.0-20241021214115-324edc3d5d38 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // indirect
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
|
47
go.sum
47
go.sum
@ -36,10 +36,12 @@ github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqw
|
|||||||
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
|
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
|
||||||
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
|
github.com/gofiber/contrib/fiberzerolog v1.0.2 h1:LMa/luarQVeINoRwZLHtLQYepLPDIwUNB5OmdZKk+s8=
|
||||||
|
github.com/gofiber/contrib/fiberzerolog v1.0.2/go.mod h1:aTPsgArSgxRWcUeJ/K6PiICz3mbQENR1QOR426QwOoQ=
|
||||||
github.com/gofiber/contrib/websocket v1.3.2 h1:AUq5PYeKwK50s0nQrnluuINYeep1c4nRCJ0NWsV3cvg=
|
github.com/gofiber/contrib/websocket v1.3.2 h1:AUq5PYeKwK50s0nQrnluuINYeep1c4nRCJ0NWsV3cvg=
|
||||||
github.com/gofiber/contrib/websocket v1.3.2/go.mod h1:07u6QGMsvX+sx7iGNCl5xhzuUVArWwLQ3tBIH24i+S8=
|
github.com/gofiber/contrib/websocket v1.3.2/go.mod h1:07u6QGMsvX+sx7iGNCl5xhzuUVArWwLQ3tBIH24i+S8=
|
||||||
github.com/gofiber/fiber/v2 v2.52.5 h1:tWoP1MJQjGEe4GB5TUGOi7P2E0ZMMRx5ZTG4rT+yGMo=
|
github.com/gofiber/fiber/v2 v2.52.6 h1:Rfp+ILPiYSvvVuIPvxrBns+HJp8qGLDnLJawAu27XVI=
|
||||||
github.com/gofiber/fiber/v2 v2.52.5/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
|
github.com/gofiber/fiber/v2 v2.52.6/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
|
||||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
||||||
@ -73,8 +75,8 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
|
|||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
@ -85,8 +87,9 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
|||||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||||
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-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
|
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||||
|
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
@ -125,8 +128,8 @@ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
|||||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||||
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
||||||
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||||
@ -155,14 +158,12 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
|
|||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||||
github.com/tinylib/msgp v1.2.4 h1:yLFeUGostXXSGW5vxfT5dXG/qzkn4schv2I7at5+hVU=
|
github.com/tinylib/msgp v1.2.5 h1:WeQg1whrXRFiZusidTQqzETkRpGjFjcIhW6uqWH09po=
|
||||||
github.com/tinylib/msgp v1.2.4/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0=
|
github.com/tinylib/msgp v1.2.5/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0=
|
||||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
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/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
github.com/valyala/fasthttp v1.57.0 h1:Xw8SjWGEP/+wAAgyy5XTvgrWlOD1+TxbbvNADYCm1Tg=
|
github.com/valyala/fasthttp v1.59.0 h1:Qu0qYHfXvPk1mSLNqcFtEk6DpxgA26hy6bmydotDpRI=
|
||||||
github.com/valyala/fasthttp v1.57.0/go.mod h1:h6ZBaPRlzpZ6O3H5t2gEk1Qi33+TmLvfwgLLp0t9CpE=
|
github.com/valyala/fasthttp v1.59.0/go.mod h1:GTxNb9Bc6r2a9D0TWNSPwDz78UxnTGBViY3xZNEqyYU=
|
||||||
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
|
|
||||||
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
|
||||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
@ -182,8 +183,8 @@ go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
|||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
||||||
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||||
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
|
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
|
||||||
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
|
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
@ -192,25 +193,25 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||||
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
package directory
|
|
||||||
|
|
||||||
const (
|
|
||||||
CommandMethodGet = "get"
|
|
||||||
CommandMethodPut = "put"
|
|
||||||
CommandMethodPatch = "patch"
|
|
||||||
CommandMethodPost = "post"
|
|
||||||
CommandMethodDelete = "delete"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Command struct {
|
|
||||||
// The unique identifier of the command, different method command can hold the same command id
|
|
||||||
ID string `json:"id"`
|
|
||||||
// The method of the command, such as get, post, others; inspired by RESTful design
|
|
||||||
Method string `json:"method"`
|
|
||||||
// The tags of the command will be used to invoke the pre-command middlewares and post-command middlewares
|
|
||||||
Tags []string `json:"tags"`
|
|
||||||
// The implementation of the command, the handler is the service that will be invoked
|
|
||||||
Handler []*ServiceInstance `json:"handler"`
|
|
||||||
|
|
||||||
RobinIndex uint `json:"robin_index"`
|
|
||||||
}
|
|
@ -1,79 +0,0 @@
|
|||||||
package directory
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/internal/kv"
|
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/nex"
|
|
||||||
"github.com/goccy/go-json"
|
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
"github.com/samber/lo"
|
|
||||||
)
|
|
||||||
|
|
||||||
const CommandInfoKvPrefix = "nexus.command/"
|
|
||||||
|
|
||||||
func AddCommand(id, method string, tags []string, handler *ServiceInstance) error {
|
|
||||||
if tags == nil {
|
|
||||||
tags = make([]string, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
ky := CommandInfoKvPrefix + nex.GetCommandKey(id, method)
|
|
||||||
|
|
||||||
command := &Command{
|
|
||||||
ID: id,
|
|
||||||
Method: method,
|
|
||||||
Tags: tags,
|
|
||||||
Handler: []*ServiceInstance{handler},
|
|
||||||
}
|
|
||||||
|
|
||||||
command.Handler = lo.UniqBy(command.Handler, func(item *ServiceInstance) string {
|
|
||||||
return item.ID
|
|
||||||
})
|
|
||||||
|
|
||||||
commandJSON, err := json.Marshal(command)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error marshaling command: %v", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = kv.Kv.Put(context.Background(), ky, string(commandJSON))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetCommandHandler(id, method string) *ServiceInstance {
|
|
||||||
ky := CommandInfoKvPrefix + nex.GetCommandKey(id, method)
|
|
||||||
|
|
||||||
resp, err := kv.Kv.Get(context.Background(), ky)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(resp.Kvs) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var command Command
|
|
||||||
if err := json.Unmarshal(resp.Kvs[0].Value, &command); err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(command.Handler) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
idx := command.RobinIndex % uint(len(command.Handler))
|
|
||||||
command.RobinIndex = idx + 1
|
|
||||||
|
|
||||||
raw, err := json.Marshal(&command)
|
|
||||||
if err == nil {
|
|
||||||
_, _ = kv.Kv.Put(context.Background(), ky, string(raw))
|
|
||||||
}
|
|
||||||
|
|
||||||
return command.Handler[idx]
|
|
||||||
}
|
|
||||||
|
|
||||||
func RemoveCommand(id, method string) error {
|
|
||||||
ky := CommandInfoKvPrefix + nex.GetCommandKey(id, method)
|
|
||||||
|
|
||||||
_, err := kv.Kv.Delete(context.Background(), ky)
|
|
||||||
return err
|
|
||||||
}
|
|
@ -1,115 +0,0 @@
|
|||||||
package directory
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/proto"
|
|
||||||
"google.golang.org/grpc/codes"
|
|
||||||
"google.golang.org/grpc/status"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type CommandRpcServer struct {
|
|
||||||
proto.UnimplementedCommandProviderServer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CommandRpcServer) AddCommand(ctx context.Context, info *proto.CommandInfo) (*proto.AddCommandResponse, error) {
|
|
||||||
clientId, err := GetClientId(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
service := GetServiceInstance(clientId)
|
|
||||||
if service == nil {
|
|
||||||
return nil, status.Errorf(codes.NotFound, "service not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
AddCommand(info.GetId(), info.GetMethod(), info.GetTags(), service)
|
|
||||||
return &proto.AddCommandResponse{
|
|
||||||
IsSuccess: true,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CommandRpcServer) RemoveCommand(ctx context.Context, request *proto.CommandLookupRequest) (*proto.RemoveCommandResponse, error) {
|
|
||||||
RemoveCommand(request.GetId(), request.GetMethod())
|
|
||||||
return &proto.RemoveCommandResponse{
|
|
||||||
IsSuccess: true,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CommandRpcServer) SendCommand(ctx context.Context, argument *proto.CommandArgument) (*proto.CommandReturn, error) {
|
|
||||||
id := argument.GetCommand()
|
|
||||||
method := argument.GetMethod()
|
|
||||||
|
|
||||||
handler := GetCommandHandler(id, method)
|
|
||||||
if handler == nil {
|
|
||||||
return &proto.CommandReturn{
|
|
||||||
IsDelivered: false,
|
|
||||||
Status: http.StatusNotFound,
|
|
||||||
ContentType: "text/plain+error",
|
|
||||||
Payload: []byte("command not found"),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
conn, err := handler.GetGrpcConn()
|
|
||||||
if err != nil {
|
|
||||||
return &proto.CommandReturn{
|
|
||||||
IsDelivered: false,
|
|
||||||
Status: http.StatusServiceUnavailable,
|
|
||||||
ContentType: "text/plain+error",
|
|
||||||
Payload: []byte("service unavailable"),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(ctx, time.Second*10)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
out, err := proto.NewCommandProviderClient(conn).SendCommand(ctx, argument)
|
|
||||||
if err != nil {
|
|
||||||
return &proto.CommandReturn{
|
|
||||||
IsDelivered: true,
|
|
||||||
Status: http.StatusInternalServerError,
|
|
||||||
ContentType: "text/plain+error",
|
|
||||||
Payload: []byte(err.Error()),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
out.IsDelivered = true
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CommandRpcServer) SendStreamCommand(g proto.CommandProvider_SendStreamCommandServer) error {
|
|
||||||
for {
|
|
||||||
pck, err := g.Recv()
|
|
||||||
if err == io.EOF {
|
|
||||||
return nil
|
|
||||||
} else if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
id := pck.GetCommand()
|
|
||||||
method := pck.GetMethod()
|
|
||||||
|
|
||||||
handler := GetCommandHandler(id, method)
|
|
||||||
if handler == nil {
|
|
||||||
return status.Errorf(codes.NotFound, "command not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
conn, err := handler.GetGrpcConn()
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(g.Context(), time.Second*10)
|
|
||||||
out, err := proto.NewCommandProviderClient(conn).SendCommand(ctx, pck)
|
|
||||||
cancel()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
_ = g.Send(&proto.CommandReturn{
|
|
||||||
IsDelivered: false,
|
|
||||||
Status: http.StatusInternalServerError,
|
|
||||||
ContentType: "text/plain+error",
|
|
||||||
Payload: []byte(err.Error()),
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
_ = g.Send(out)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +1,34 @@
|
|||||||
package directory
|
package directory
|
||||||
|
|
||||||
import "github.com/rs/zerolog/log"
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
var statusOfServices = make(map[string]bool)
|
||||||
|
var statusLock sync.Mutex
|
||||||
|
|
||||||
|
func GetServiceStatus() map[string]bool {
|
||||||
|
out := make(map[string]bool)
|
||||||
|
for k, v := range statusOfServices {
|
||||||
|
out[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
services := ListServiceInstance()
|
||||||
|
for _, service := range services {
|
||||||
|
if _, ok := out[service.Type]; !ok {
|
||||||
|
out[service.Type] = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
func ValidateServices() {
|
func ValidateServices() {
|
||||||
|
statusLock.Lock()
|
||||||
|
defer statusLock.Unlock()
|
||||||
|
|
||||||
services := ListServiceInstance()
|
services := ListServiceInstance()
|
||||||
if len(services) == 0 {
|
if len(services) == 0 {
|
||||||
return
|
return
|
||||||
@ -19,9 +45,12 @@ func ValidateServices() {
|
|||||||
}
|
}
|
||||||
// Directly use the connect method to skip cache
|
// Directly use the connect method to skip cache
|
||||||
if _, err := ConnectService(service); err != nil {
|
if _, err := ConnectService(service); err != nil {
|
||||||
|
statusOfServices[service.Type] = false
|
||||||
_ = RemoveServiceInstance(service.ID)
|
_ = RemoveServiceInstance(service.ID)
|
||||||
log.Warn().Err(err).Str("id", service.ID).Str("addr", service.GrpcAddr).Msg("Unable connect to service, dropped...")
|
log.Warn().Err(err).Str("id", service.ID).Str("addr", service.GrpcAddr).Msg("Unable connect to service, dropped...")
|
||||||
continue
|
continue
|
||||||
|
} else {
|
||||||
|
statusOfServices[service.Type] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
successCount++
|
successCount++
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
package grpc
|
package grpc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/internal/directory"
|
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/internal/directory"
|
||||||
|
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/proto"
|
"git.solsynth.dev/hypernet/nexus/pkg/proto"
|
||||||
|
|
||||||
"google.golang.org/grpc/reflection"
|
"google.golang.org/grpc/reflection"
|
||||||
@ -29,7 +30,6 @@ func NewServer() *Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
proto.RegisterDirectoryServiceServer(server.srv, &directory.ServiceRpcServer{})
|
proto.RegisterDirectoryServiceServer(server.srv, &directory.ServiceRpcServer{})
|
||||||
proto.RegisterCommandProviderServer(server.srv, &directory.CommandRpcServer{})
|
|
||||||
proto.RegisterDatabaseServiceServer(server.srv, server)
|
proto.RegisterDatabaseServiceServer(server.srv, server)
|
||||||
proto.RegisterStreamServiceServer(server.srv, server)
|
proto.RegisterStreamServiceServer(server.srv, server)
|
||||||
proto.RegisterAllocatorServiceServer(server.srv, server)
|
proto.RegisterAllocatorServiceServer(server.srv, server)
|
||||||
|
@ -4,7 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/internal/http/ws"
|
"git.solsynth.dev/hypernet/nexus/pkg/internal/web/ws"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
|
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/proto"
|
"git.solsynth.dev/hypernet/nexus/pkg/proto"
|
||||||
|
@ -1,67 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/internal/directory"
|
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/proto"
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
"google.golang.org/grpc/metadata"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func invokeCommand(c *fiber.Ctx) error {
|
|
||||||
command := c.Params("command")
|
|
||||||
method := strings.ToLower(c.Method())
|
|
||||||
|
|
||||||
handler := directory.GetCommandHandler(command, method)
|
|
||||||
if handler == nil {
|
|
||||||
return fiber.NewError(fiber.StatusNotFound, "command not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
conn, err := handler.GetGrpcConn()
|
|
||||||
if err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusServiceUnavailable, "service unavailable")
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debug().Str("id", command).Str("method", method).Msg("Invoking command from HTTP Gateway...")
|
|
||||||
|
|
||||||
var meta []string
|
|
||||||
meta = append(meta, "client_id", "http-gateway")
|
|
||||||
meta = append(meta, "net.ip", c.IP())
|
|
||||||
meta = append(meta, "http.user_agent", c.Get(fiber.HeaderUserAgent))
|
|
||||||
for k, v := range c.GetReqHeaders() {
|
|
||||||
meta = append(
|
|
||||||
meta,
|
|
||||||
strings.ToLower(fmt.Sprintf("header.%s", strings.ReplaceAll(k, "-", "_"))),
|
|
||||||
strings.Join(v, "\n"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
for k, v := range c.Queries() {
|
|
||||||
meta = append(
|
|
||||||
meta,
|
|
||||||
strings.ToLower(fmt.Sprintf("query.%s", strings.ReplaceAll(k, "-", "_"))),
|
|
||||||
v,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := metadata.AppendToOutgoingContext(c.Context(), meta...)
|
|
||||||
ctx, cancel := context.WithTimeout(ctx, time.Second*10)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
out, err := proto.NewCommandProviderClient(conn).SendCommand(ctx, &proto.CommandArgument{
|
|
||||||
Command: command,
|
|
||||||
Method: method,
|
|
||||||
Payload: c.Body(),
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
|
||||||
} else {
|
|
||||||
c.Set(fiber.HeaderContentType, out.ContentType)
|
|
||||||
return c.Status(int(out.Status)).Send(out.Payload)
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,27 +4,28 @@ import (
|
|||||||
pkg "git.solsynth.dev/hypernet/nexus/pkg/internal"
|
pkg "git.solsynth.dev/hypernet/nexus/pkg/internal"
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/internal/auth"
|
"git.solsynth.dev/hypernet/nexus/pkg/internal/auth"
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/internal/directory"
|
"git.solsynth.dev/hypernet/nexus/pkg/internal/directory"
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/internal/http/ws"
|
"git.solsynth.dev/hypernet/nexus/pkg/internal/web/ws"
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/nex"
|
"git.solsynth.dev/hypernet/nexus/pkg/nex"
|
||||||
"github.com/gofiber/contrib/websocket"
|
"github.com/gofiber/contrib/websocket"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/gofiber/fiber/v2/middleware/proxy"
|
"github.com/gofiber/fiber/v2/middleware/proxy"
|
||||||
)
|
)
|
||||||
|
|
||||||
func MapAPIs(app *fiber.App) {
|
func MapControllers(app *fiber.App) {
|
||||||
|
app.Get("/check-ip", getClientIP)
|
||||||
|
app.Get("/", func(c *fiber.Ctx) error {
|
||||||
|
return c.JSON(fiber.Map{
|
||||||
|
"api_level": pkg.ApiLevel,
|
||||||
|
"version": pkg.AppVersion,
|
||||||
|
"status": true,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
app.Get("/directory/status", getServicesStatus)
|
||||||
|
app.Get("/directory/services", listExistsService)
|
||||||
|
|
||||||
// Some built-in public-accessible APIs
|
// Some built-in public-accessible APIs
|
||||||
wellKnown := app.Group("/.well-known").Name("Well Known")
|
wellKnown := app.Group("/.well-known").Name("Well Known")
|
||||||
{
|
{
|
||||||
wellKnown.Get("/", func(c *fiber.Ctx) error {
|
|
||||||
return c.JSON(fiber.Map{
|
|
||||||
"api_level": pkg.ApiLevel,
|
|
||||||
"version": pkg.AppVersion,
|
|
||||||
"status": true,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
wellKnown.Get("/check-ip", getClientIP)
|
|
||||||
wellKnown.Get("/directory/services", listExistsService)
|
|
||||||
|
|
||||||
wellKnown.Get("/openid-configuration", func(c *fiber.Ctx) error {
|
wellKnown.Get("/openid-configuration", func(c *fiber.Ctx) error {
|
||||||
service := directory.GetServiceInstanceByType(nex.ServiceTypeAuth)
|
service := directory.GetServiceInstanceByType(nex.ServiceTypeAuth)
|
||||||
if service == nil || service.HttpAddr == nil {
|
if service == nil || service.HttpAddr == nil {
|
||||||
@ -44,12 +45,11 @@ func MapAPIs(app *fiber.App) {
|
|||||||
// WatchTower administration APIs
|
// WatchTower administration APIs
|
||||||
wt := app.Group("/wt").Name("WatchTower").Use(auth.ValidatorMiddleware)
|
wt := app.Group("/wt").Name("WatchTower").Use(auth.ValidatorMiddleware)
|
||||||
{
|
{
|
||||||
wt.Post("/maintenance/database", wtRunDbMaintenance)
|
wt.Post("/maintenance/database", watchRunDbMaintenance)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Common websocket gateway
|
// Common websocket gateway
|
||||||
app.Get("/ws", auth.ValidatorMiddleware, websocket.New(ws.Listen))
|
app.Get("/ws", auth.ValidatorMiddleware, websocket.New(ws.Listen))
|
||||||
|
|
||||||
app.All("/inv/:command", invokeCommand)
|
|
||||||
app.All("/cgi/:service/*", forwardService)
|
app.All("/cgi/:service/*", forwardService)
|
||||||
}
|
}
|
10
pkg/internal/web/api/status.go
Normal file
10
pkg/internal/web/api/status.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/internal/directory"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getServicesStatus(c *fiber.Ctx) error {
|
||||||
|
return c.JSON(directory.GetServiceStatus())
|
||||||
|
}
|
@ -6,7 +6,7 @@ import (
|
|||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func wtRunDbMaintenance(c *fiber.Ctx) error {
|
func watchRunDbMaintenance(c *fiber.Ctx) error {
|
||||||
if err := sec.EnsureGrantedPerm(c, "AdminOperateWatchTower", true); err != nil {
|
if err := sec.EnsureGrantedPerm(c, "AdminOperateWatchTower", true); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
@ -1,30 +1,30 @@
|
|||||||
package server
|
package web
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/internal/auth"
|
"git.solsynth.dev/hypernet/nexus/pkg/internal/auth"
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/internal/http/api"
|
"git.solsynth.dev/hypernet/nexus/pkg/internal/web/api"
|
||||||
"github.com/goccy/go-json"
|
"github.com/goccy/go-json"
|
||||||
|
"github.com/gofiber/contrib/fiberzerolog"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/gofiber/fiber/v2/middleware/cors"
|
"github.com/gofiber/fiber/v2/middleware/cors"
|
||||||
"github.com/gofiber/fiber/v2/middleware/idempotency"
|
"github.com/gofiber/fiber/v2/middleware/idempotency"
|
||||||
"github.com/gofiber/fiber/v2/middleware/limiter"
|
"github.com/gofiber/fiber/v2/middleware/limiter"
|
||||||
"github.com/gofiber/fiber/v2/middleware/logger"
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
type HTTPApp struct {
|
type WebApp struct {
|
||||||
app *fiber.App
|
app *fiber.App
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServer() *HTTPApp {
|
func NewServer() *WebApp {
|
||||||
app := fiber.New(fiber.Config{
|
app := fiber.New(fiber.Config{
|
||||||
DisableStartupMessage: true,
|
DisableStartupMessage: true,
|
||||||
EnableIPValidation: true,
|
EnableIPValidation: true,
|
||||||
ServerHeader: "Hypernet.Nexus",
|
ServerHeader: "HyperNet.Nexus",
|
||||||
AppName: "Hypernet.Nexus",
|
AppName: "HyperNet.Nexus",
|
||||||
ProxyHeader: fiber.HeaderXForwardedFor,
|
ProxyHeader: fiber.HeaderXForwardedFor,
|
||||||
JSONEncoder: json.Marshal,
|
JSONEncoder: json.Marshal,
|
||||||
JSONDecoder: json.Unmarshal,
|
JSONDecoder: json.Unmarshal,
|
||||||
@ -32,6 +32,10 @@ func NewServer() *HTTPApp {
|
|||||||
EnablePrintRoutes: viper.GetBool("debug.print_routes"),
|
EnablePrintRoutes: viper.GetBool("debug.print_routes"),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
app.Use(fiberzerolog.New(fiberzerolog.Config{
|
||||||
|
Logger: &log.Logger,
|
||||||
|
}))
|
||||||
|
|
||||||
app.Use(idempotency.New())
|
app.Use(idempotency.New())
|
||||||
app.Use(cors.New(cors.Config{
|
app.Use(cors.New(cors.Config{
|
||||||
AllowCredentials: true,
|
AllowCredentials: true,
|
||||||
@ -41,11 +45,6 @@ func NewServer() *HTTPApp {
|
|||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
app.Use(logger.New(logger.Config{
|
|
||||||
Format: "${status} | ${latency} | ${method} ${path}\n",
|
|
||||||
Output: log.Logger,
|
|
||||||
}))
|
|
||||||
|
|
||||||
app.Use(auth.ContextMiddleware)
|
app.Use(auth.ContextMiddleware)
|
||||||
app.Use(limiter.New(limiter.Config{
|
app.Use(limiter.New(limiter.Config{
|
||||||
Max: viper.GetInt("rate_limit"),
|
Max: viper.GetInt("rate_limit"),
|
||||||
@ -53,12 +52,12 @@ func NewServer() *HTTPApp {
|
|||||||
LimiterMiddleware: limiter.SlidingWindow{},
|
LimiterMiddleware: limiter.SlidingWindow{},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
api.MapAPIs(app)
|
api.MapControllers(app)
|
||||||
|
|
||||||
return &HTTPApp{app}
|
return &WebApp{app}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *HTTPApp) Listen() {
|
func (v *WebApp) Listen() {
|
||||||
if err := v.app.Listen(viper.GetString("bind")); err != nil {
|
if err := v.app.Listen(viper.GetString("bind")); err != nil {
|
||||||
log.Fatal().Err(err).Msg("An error occurred when starting server...")
|
log.Fatal().Err(err).Msg("An error occurred when starting server...")
|
||||||
}
|
}
|
@ -9,10 +9,10 @@ import (
|
|||||||
"git.solsynth.dev/hypernet/nexus/pkg/internal/auth"
|
"git.solsynth.dev/hypernet/nexus/pkg/internal/auth"
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/internal/database"
|
"git.solsynth.dev/hypernet/nexus/pkg/internal/database"
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/internal/directory"
|
"git.solsynth.dev/hypernet/nexus/pkg/internal/directory"
|
||||||
server "git.solsynth.dev/hypernet/nexus/pkg/internal/http"
|
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/internal/kv"
|
"git.solsynth.dev/hypernet/nexus/pkg/internal/kv"
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/internal/mq"
|
"git.solsynth.dev/hypernet/nexus/pkg/internal/mq"
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/internal/watchtower"
|
"git.solsynth.dev/hypernet/nexus/pkg/internal/watchtower"
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/internal/web"
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/nex/sec"
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/sec"
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
|
|
||||||
@ -109,7 +109,7 @@ func main() {
|
|||||||
go directory.ValidateServices()
|
go directory.ValidateServices()
|
||||||
|
|
||||||
// Server
|
// Server
|
||||||
go server.NewServer().Listen()
|
go web.NewServer().Listen()
|
||||||
|
|
||||||
// Grpc Server
|
// Grpc Server
|
||||||
go grpc.NewServer().Listen()
|
go grpc.NewServer().Listen()
|
||||||
@ -117,6 +117,7 @@ func main() {
|
|||||||
// Configure timed tasks
|
// Configure timed tasks
|
||||||
quartz := cron.New(cron.WithLogger(cron.VerbosePrintfLogger(&log.Logger)))
|
quartz := cron.New(cron.WithLogger(cron.VerbosePrintfLogger(&log.Logger)))
|
||||||
quartz.AddFunc("@midnight", watchtower.RunDbMaintenance)
|
quartz.AddFunc("@midnight", watchtower.RunDbMaintenance)
|
||||||
|
quartz.AddFunc("@every 5m", directory.ValidateServices)
|
||||||
quartz.Start()
|
quartz.Start()
|
||||||
|
|
||||||
// Messages
|
// Messages
|
||||||
|
@ -1,150 +0,0 @@
|
|||||||
package nex
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/proto"
|
|
||||||
"google.golang.org/grpc"
|
|
||||||
health "google.golang.org/grpc/health/grpc_health_v1"
|
|
||||||
"google.golang.org/grpc/metadata"
|
|
||||||
"google.golang.org/grpc/reflection"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type CommandHandler func(ctx *CommandCtx) error
|
|
||||||
|
|
||||||
func GetCommandKey(id, method string) string {
|
|
||||||
return id + ":" + method
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *Conn) AddCommand(id, method string, tags []string, fn CommandHandler) error {
|
|
||||||
method = strings.ToLower(method)
|
|
||||||
dir := proto.NewCommandProviderClient(v.nexusConn)
|
|
||||||
ctx := context.Background()
|
|
||||||
ctx = metadata.AppendToOutgoingContext(ctx, "client_id", v.Info.Id)
|
|
||||||
|
|
||||||
var addingMethodQueue []string
|
|
||||||
if method == "all" {
|
|
||||||
addingMethodQueue = []string{"get", "post", "put", "patch", "delete"}
|
|
||||||
} else {
|
|
||||||
addingMethodQueue = append(addingMethodQueue, method)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, method := range addingMethodQueue {
|
|
||||||
ky := GetCommandKey(id, method)
|
|
||||||
_, err := dir.AddCommand(ctx, &proto.CommandInfo{
|
|
||||||
Id: id,
|
|
||||||
Method: method,
|
|
||||||
Tags: tags,
|
|
||||||
})
|
|
||||||
if err == nil {
|
|
||||||
v.commandHandlers[ky] = fn
|
|
||||||
} else {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type localCommandRpcServer struct {
|
|
||||||
conn *Conn
|
|
||||||
|
|
||||||
proto.UnimplementedCommandProviderServer
|
|
||||||
health.UnimplementedHealthServer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v localCommandRpcServer) SendCommand(ctx context.Context, argument *proto.CommandArgument) (*proto.CommandReturn, error) {
|
|
||||||
ky := GetCommandKey(argument.GetCommand(), argument.GetMethod())
|
|
||||||
if handler, ok := v.conn.commandHandlers[ky]; !ok {
|
|
||||||
return &proto.CommandReturn{
|
|
||||||
Status: http.StatusNotFound,
|
|
||||||
Payload: []byte(argument.GetCommand() + " not found"),
|
|
||||||
}, nil
|
|
||||||
} else {
|
|
||||||
cc := &CommandCtx{
|
|
||||||
requestBody: argument.GetPayload(),
|
|
||||||
statusCode: http.StatusOK,
|
|
||||||
}
|
|
||||||
if md, ok := metadata.FromIncomingContext(ctx); ok {
|
|
||||||
for k, v := range md {
|
|
||||||
var val any = nil
|
|
||||||
if len(v) == 1 {
|
|
||||||
if len(v[0]) != 0 {
|
|
||||||
if i, err := strconv.ParseInt(v[0], 10, 64); err == nil {
|
|
||||||
val = i
|
|
||||||
} else if b, err := strconv.ParseBool(v[0]); err == nil {
|
|
||||||
val = b
|
|
||||||
} else if f, err := strconv.ParseFloat(v[0], 64); err == nil {
|
|
||||||
val = f
|
|
||||||
}
|
|
||||||
layouts := []string{
|
|
||||||
time.RFC3339,
|
|
||||||
"2006-01-02 15:04:05", // Example: 2024-10-20 14:55:05
|
|
||||||
"2006-01-02", // Example: 2024-10-20
|
|
||||||
}
|
|
||||||
for _, layout := range layouts {
|
|
||||||
if t, err := time.Parse(layout, v[0]); err == nil {
|
|
||||||
val = t
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if val == nil {
|
|
||||||
val = v[0]
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val = v[0]
|
|
||||||
}
|
|
||||||
} else if len(v) > 1 {
|
|
||||||
val = v
|
|
||||||
}
|
|
||||||
cc.values.Store(k, val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := handler(cc); err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else {
|
|
||||||
return &proto.CommandReturn{
|
|
||||||
Status: int32(cc.statusCode),
|
|
||||||
ContentType: cc.contentType,
|
|
||||||
Payload: cc.responseBody,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v localCommandRpcServer) Check(ctx context.Context, request *health.HealthCheckRequest) (*health.HealthCheckResponse, error) {
|
|
||||||
return &health.HealthCheckResponse{
|
|
||||||
Status: health.HealthCheckResponse_SERVING,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v localCommandRpcServer) Watch(request *health.HealthCheckRequest, server health.Health_WatchServer) error {
|
|
||||||
for {
|
|
||||||
if server.Send(&health.HealthCheckResponse{
|
|
||||||
Status: health.HealthCheckResponse_SERVING,
|
|
||||||
}) != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
time.Sleep(1000 * time.Millisecond)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *Conn) RunCommands(addr string) error {
|
|
||||||
v.commandServer = grpc.NewServer()
|
|
||||||
service := &localCommandRpcServer{conn: v}
|
|
||||||
proto.RegisterCommandProviderServer(v.commandServer, service)
|
|
||||||
health.RegisterHealthServer(v.commandServer, service)
|
|
||||||
reflection.Register(v.commandServer)
|
|
||||||
|
|
||||||
listener, err := net.Listen("tcp", addr)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return v.commandServer.Serve(listener)
|
|
||||||
}
|
|
@ -1,90 +0,0 @@
|
|||||||
package nex
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/goccy/go-json"
|
|
||||||
"net/http"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
type CommandCtx struct {
|
|
||||||
requestBody []byte
|
|
||||||
responseBody []byte
|
|
||||||
|
|
||||||
contentType string
|
|
||||||
statusCode int
|
|
||||||
|
|
||||||
values sync.Map
|
|
||||||
}
|
|
||||||
|
|
||||||
func CtxValueMustBe[T any](c *CommandCtx, key string) (T, error) {
|
|
||||||
if val, ok := c.values.Load(key); ok {
|
|
||||||
if v, ok := val.(T); ok {
|
|
||||||
return v, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var out T
|
|
||||||
if err := c.Write([]byte(fmt.Sprintf("value %s not found in type %T", key, out)), "text/plain+error", http.StatusBadRequest); err != nil {
|
|
||||||
return out, err
|
|
||||||
}
|
|
||||||
return out, fmt.Errorf("value %s not found", key)
|
|
||||||
}
|
|
||||||
|
|
||||||
func CtxValueShouldBe[T any](c *CommandCtx, key string, defaultValue T) T {
|
|
||||||
if val, ok := c.values.Load(key); ok {
|
|
||||||
if v, ok := val.(T); ok {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CommandCtx) Values() map[string]any {
|
|
||||||
duplicate := make(map[string]any)
|
|
||||||
c.values.Range(func(key, value any) bool {
|
|
||||||
duplicate[key.(string)] = value
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
return duplicate
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CommandCtx) ValueOrElse(key string, defaultValue any) any {
|
|
||||||
val, _ := c.values.Load(key)
|
|
||||||
if val == nil {
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CommandCtx) Value(key string, newValue ...any) any {
|
|
||||||
if len(newValue) > 0 {
|
|
||||||
c.values.Store(key, newValue[0])
|
|
||||||
}
|
|
||||||
val, _ := c.values.Load(key)
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CommandCtx) Read() []byte {
|
|
||||||
return c.requestBody
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CommandCtx) ReadJSON(out any) error {
|
|
||||||
return json.Unmarshal(c.requestBody, out)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CommandCtx) Write(data []byte, contentType string, statusCode ...int) error {
|
|
||||||
c.responseBody = data
|
|
||||||
c.contentType = contentType
|
|
||||||
if len(statusCode) > 0 {
|
|
||||||
c.statusCode = statusCode[0]
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CommandCtx) JSON(data any, statusCode ...int) error {
|
|
||||||
raw, err := json.Marshal(data)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return c.Write(raw, "application/json", statusCode...)
|
|
||||||
}
|
|
@ -1,60 +0,0 @@
|
|||||||
package nex_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/nex"
|
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/proto"
|
|
||||||
"net/http"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestHandleCommand(t *testing.T) {
|
|
||||||
conn, err := nex.NewNexusConn("127.0.0.1:7001", &proto.ServiceInfo{
|
|
||||||
Id: "echo01",
|
|
||||||
Type: "echo",
|
|
||||||
Label: "Echo",
|
|
||||||
GrpcAddr: "127.0.0.1:6001",
|
|
||||||
HttpAddr: nil,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(fmt.Errorf("unable to connect nexus: %v", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := conn.RegisterService(); err != nil {
|
|
||||||
t.Fatal(fmt.Errorf("unable to register service: %v", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
err = conn.AddCommand("say.hi", "all", nil, func(ctx *nex.CommandCtx) error {
|
|
||||||
return ctx.Write([]byte("Hello, World!"), "text/plain", http.StatusOK)
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(fmt.Errorf("unable to add command: %v", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = conn.AddCommand("echo", "all", nil, func(ctx *nex.CommandCtx) error {
|
|
||||||
t.Log("Received command: ", string(ctx.Read()))
|
|
||||||
return ctx.Write(ctx.Read(), "text/plain", http.StatusOK)
|
|
||||||
})
|
|
||||||
err = conn.AddCommand("echo.details", "all", nil, func(ctx *nex.CommandCtx) error {
|
|
||||||
return ctx.JSON(map[string]any{
|
|
||||||
"values": ctx.Values(),
|
|
||||||
"body": ctx.Read(),
|
|
||||||
}, http.StatusOK)
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(fmt.Errorf("unable to add command: %v", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
err := conn.RunCommands("0.0.0.0:6001")
|
|
||||||
if err != nil {
|
|
||||||
t.Error(fmt.Errorf("unable to run commands: %v", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
t.Log("Waiting 60 seconds for calling command...")
|
|
||||||
time.Sleep(time.Second * 60)
|
|
||||||
}
|
|
@ -2,9 +2,10 @@ package nex
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"google.golang.org/grpc/metadata"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"google.golang.org/grpc/metadata"
|
||||||
|
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/proto"
|
"git.solsynth.dev/hypernet/nexus/pkg/proto"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/credentials/insecure"
|
"google.golang.org/grpc/credentials/insecure"
|
||||||
@ -15,9 +16,6 @@ type Conn struct {
|
|||||||
Addr string
|
Addr string
|
||||||
Info *proto.ServiceInfo
|
Info *proto.ServiceInfo
|
||||||
|
|
||||||
commandServer *grpc.Server
|
|
||||||
commandHandlers map[string]CommandHandler
|
|
||||||
|
|
||||||
nexusConn *grpc.ClientConn
|
nexusConn *grpc.ClientConn
|
||||||
clientConn map[string]*grpc.ClientConn
|
clientConn map[string]*grpc.ClientConn
|
||||||
}
|
}
|
||||||
@ -35,8 +33,6 @@ func NewNexusConn(addr string, info *proto.ServiceInfo) (*Conn, error) {
|
|||||||
Addr: addr,
|
Addr: addr,
|
||||||
Info: info,
|
Info: info,
|
||||||
|
|
||||||
commandHandlers: make(map[string]CommandHandler),
|
|
||||||
|
|
||||||
nexusConn: conn,
|
nexusConn: conn,
|
||||||
clientConn: make(map[string]*grpc.ClientConn),
|
clientConn: make(map[string]*grpc.ClientConn),
|
||||||
}, nil
|
}, nil
|
||||||
|
@ -1,130 +0,0 @@
|
|||||||
package cruda
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/nex"
|
|
||||||
"github.com/go-playground/validator/v10"
|
|
||||||
"gorm.io/gorm"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
type CrudAction func(v *CrudConn) nex.CommandHandler
|
|
||||||
|
|
||||||
func AddModel[T any](v *CrudConn, model T, id, prefix string, tags []string) error {
|
|
||||||
funcList := []CrudAction{cmdList[T], cmdGet[T], cmdCreate[T], cmdUpdate[T], cmdDelete[T]}
|
|
||||||
funcCmds := []string{".list", "", "", "", ""}
|
|
||||||
funcMethods := []string{"get", "get", "put", "patch", "delete"}
|
|
||||||
for idx, fn := range funcList {
|
|
||||||
if err := v.n.AddCommand(prefix+id+funcCmds[idx], funcMethods[idx], tags, fn(v)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var validate = validator.New(validator.WithRequiredStructEnabled())
|
|
||||||
|
|
||||||
func cmdList[T any](c *CrudConn) nex.CommandHandler {
|
|
||||||
return func(ctx *nex.CommandCtx) error {
|
|
||||||
take := int(nex.CtxValueShouldBe[int64](ctx, "query.take", 10))
|
|
||||||
skip := int(nex.CtxValueShouldBe[int64](ctx, "query.skip", 0))
|
|
||||||
|
|
||||||
var str T
|
|
||||||
var count int64
|
|
||||||
if err := c.Db.Model(str).Count(&count).Error; err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var out []T
|
|
||||||
if err := c.Db.Offset(skip).Limit(take).Find(&out).Error; err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return ctx.JSON(map[string]any{
|
|
||||||
"count": count,
|
|
||||||
"data": out,
|
|
||||||
}, http.StatusOK)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func cmdGet[T any](c *CrudConn) nex.CommandHandler {
|
|
||||||
return func(ctx *nex.CommandCtx) error {
|
|
||||||
id, err := nex.CtxValueMustBe[int64](ctx, "query.id")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var out T
|
|
||||||
if err := c.Db.First(&out, "id = ?", id).Error; err != nil {
|
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
||||||
return ctx.Write([]byte(err.Error()), "text/plain", http.StatusNotFound)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return ctx.JSON(out, http.StatusOK)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func cmdCreate[T any](c *CrudConn) nex.CommandHandler {
|
|
||||||
return func(ctx *nex.CommandCtx) error {
|
|
||||||
var payload T
|
|
||||||
if err := ctx.ReadJSON(&payload); err != nil {
|
|
||||||
return err
|
|
||||||
} else if err := validate.Struct(payload); err != nil {
|
|
||||||
return ctx.Write([]byte(err.Error()), "text/plain+error", http.StatusBadRequest)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := c.Db.Create(&payload).Error; err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return ctx.JSON(payload, http.StatusOK)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func cmdUpdate[T any](c *CrudConn) nex.CommandHandler {
|
|
||||||
return func(ctx *nex.CommandCtx) error {
|
|
||||||
id, err := nex.CtxValueMustBe[int64](ctx, "query.id")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var payload T
|
|
||||||
if err := ctx.ReadJSON(&payload); err != nil {
|
|
||||||
return err
|
|
||||||
} else if err := validate.Struct(payload); err != nil {
|
|
||||||
return ctx.Write([]byte(err.Error()), "text/plain+error", http.StatusBadRequest)
|
|
||||||
}
|
|
||||||
|
|
||||||
var out T
|
|
||||||
if err := c.Db.Model(out).Where("id = ?", id).Updates(&payload).Error; err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := c.Db.First(&out, "id = ?", id).Error; err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return ctx.JSON(out, http.StatusOK)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func cmdDelete[T any](c *CrudConn) nex.CommandHandler {
|
|
||||||
return func(ctx *nex.CommandCtx) error {
|
|
||||||
id, err := nex.CtxValueMustBe[int64](ctx, "query.id")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var out T
|
|
||||||
if err := c.Db.Delete(&out, "id = ?", id).Error; err != nil {
|
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
||||||
return ctx.Write([]byte(err.Error()), "text/plain", http.StatusNotFound)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return ctx.Write(nil, "text/plain", http.StatusOK)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,58 +0,0 @@
|
|||||||
package cruda_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/nex"
|
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/nex/cruda"
|
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/proto"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Test struct {
|
|
||||||
cruda.BaseModel
|
|
||||||
Content string `json:"content" validate:"required"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCrudaCommand(t *testing.T) {
|
|
||||||
conn, err := nex.NewNexusConn("127.0.0.1:7001", &proto.ServiceInfo{
|
|
||||||
Id: "cruda01",
|
|
||||||
Type: "cruda",
|
|
||||||
Label: "CRUD Accelerator",
|
|
||||||
GrpcAddr: "127.0.0.1:6001",
|
|
||||||
HttpAddr: nil,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(fmt.Errorf("unable to connect nexus: %v", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := conn.RegisterService(); err != nil {
|
|
||||||
t.Fatal(fmt.Errorf("unable to register service: %v", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
cc := cruda.NewCrudaConn(conn)
|
|
||||||
dsn, err := cc.AllocDatabase("test")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(fmt.Errorf("unable to allocate database: %v", err))
|
|
||||||
}
|
|
||||||
t.Log(fmt.Sprintf("Allocated database: %s", dsn))
|
|
||||||
|
|
||||||
if err := cruda.MigrateModel(cc, Test{}); err != nil {
|
|
||||||
t.Fatal(fmt.Errorf("unable to migrate database: %v", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := cruda.AddModel(cc, Test{}, "tm", "test.", nil); err != nil {
|
|
||||||
t.Fatal(fmt.Errorf("unable to add commands: %v", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
err := conn.RunCommands("0.0.0.0:6001")
|
|
||||||
if err != nil {
|
|
||||||
t.Error(fmt.Errorf("unable to run commands: %v", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
t.Log("Waiting 180 seconds for calling command...")
|
|
||||||
time.Sleep(time.Second * 180)
|
|
||||||
}
|
|
Reference in New Issue
Block a user