From 56cfd42bb99879f19f396e341a0af9e59ae624ac Mon Sep 17 00:00:00 2001 From: loveuer Date: Fri, 1 Nov 2024 17:47:33 +0800 Subject: [PATCH] =?UTF-8?q?structure:=20=E7=A1=AE=E5=AE=9A=E5=9F=BA?= =?UTF-8?q?=E6=9C=AC=E7=BB=93=E6=9E=84(=E4=BF=9D=E6=8C=81=E5=9F=BA?= =?UTF-8?q?=E6=9C=AC=E5=BD=A2=E5=BC=8F,=20=E9=87=87=E7=94=A8=E7=BB=84?= =?UTF-8?q?=E5=90=88)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 6 +- go.sum | 199 ++++++++++++++++++ httptest/uauth/127.0.0.1 | 0 httptest/uauth/auth req.bru | 18 -- httptest/uauth/bruno.json | 9 - internal/client/client.go | 2 +- internal/cmd/serve.go | 12 +- internal/interfaces/enum.go | 11 - internal/interfaces/logger.go | 7 - internal/serve/handler/approve.go | 11 +- internal/serve/handler/authorize.go | 11 +- internal/serve/handler/login.go | 12 +- internal/serve/handler/registry.go | 10 +- internal/serve/handler/token.go | 15 +- internal/serve/serve.go | 4 +- internal/store/cache/client.go | 42 ---- internal/store/db/db_test.go | 9 - internal/tool/slice_test.go | 1 - model/init.go | 2 +- model/privilege.go | 47 +++++ model/privilege_test.go | 74 +++++++ model/role.go | 13 ++ model/scope.go | 11 + model/user.go | 14 +- {internal/store => pkg}/cache/cache_lru.go | 9 +- {internal/store => pkg}/cache/cache_memory.go | 9 +- {internal/store => pkg}/cache/cache_redis.go | 5 +- .../database.go => pkg/cache/client.go | 42 +++- {internal/store => pkg}/cache/error.go | 0 {internal/store => pkg}/cache/init.go | 31 +-- {internal/store => pkg}/cache/scan.go | 0 {internal => pkg}/middleware/auth/auth.go | 4 +- pkg/middleware/logger/logger.go | 47 +++++ pkg/rbac/create.go | 110 ++++++++++ pkg/rbac/rbac.go | 83 ++++++++ pkg/sqlType/error.go | 9 + pkg/sqlType/jsonb.go | 76 +++++++ pkg/sqlType/num_slice.go | 71 +++++++ pkg/sqlType/string_slice.go | 107 ++++++++++ {internal/store/db => pkg/store}/client.go | 19 +- {internal/store/db => pkg/store}/init.go | 17 +- internal/tool/slice.go => tool/bulk.go | 0 {internal/tool => tool}/cert.go | 0 {internal/tool => tool}/ctx.go | 0 {internal/tool => tool}/file.go | 0 {internal/tool => tool}/human.go | 0 {internal/tool => tool}/must.go | 0 {internal/tool => tool}/password.go | 0 {internal/tool => tool}/password_test.go | 0 {internal/tool => tool}/random.go | 0 {internal/tool => tool}/table.go | 0 {internal/tool => tool}/time.go | 0 52 files changed, 1003 insertions(+), 176 deletions(-) delete mode 100644 httptest/uauth/127.0.0.1 delete mode 100644 httptest/uauth/auth req.bru delete mode 100644 httptest/uauth/bruno.json delete mode 100644 internal/interfaces/enum.go delete mode 100644 internal/interfaces/logger.go delete mode 100644 internal/store/cache/client.go delete mode 100644 internal/store/db/db_test.go delete mode 100644 internal/tool/slice_test.go create mode 100644 model/privilege.go create mode 100644 model/privilege_test.go create mode 100644 model/role.go create mode 100644 model/scope.go rename {internal/store => pkg}/cache/cache_lru.go (89%) rename {internal/store => pkg}/cache/cache_memory.go (86%) rename {internal/store => pkg}/cache/cache_redis.go (90%) rename internal/interfaces/database.go => pkg/cache/client.go (56%) rename {internal/store => pkg}/cache/error.go (100%) rename {internal/store => pkg}/cache/init.go (54%) rename {internal/store => pkg}/cache/scan.go (100%) rename {internal => pkg}/middleware/auth/auth.go (97%) create mode 100644 pkg/middleware/logger/logger.go create mode 100644 pkg/rbac/create.go create mode 100644 pkg/rbac/rbac.go create mode 100644 pkg/sqlType/error.go create mode 100644 pkg/sqlType/jsonb.go create mode 100644 pkg/sqlType/num_slice.go create mode 100644 pkg/sqlType/string_slice.go rename {internal/store/db => pkg/store}/client.go (63%) rename {internal/store/db => pkg/store}/init.go (73%) rename internal/tool/slice.go => tool/bulk.go (100%) rename {internal/tool => tool}/cert.go (100%) rename {internal/tool => tool}/ctx.go (100%) rename {internal/tool => tool}/file.go (100%) rename {internal/tool => tool}/human.go (100%) rename {internal/tool => tool}/must.go (100%) rename {internal/tool => tool}/password.go (100%) rename {internal/tool => tool}/password_test.go (100%) rename {internal/tool => tool}/random.go (100%) rename {internal/tool => tool}/table.go (100%) rename {internal/tool => tool}/time.go (100%) diff --git a/go.mod b/go.mod index 2b957ed..b13e4de 100644 --- a/go.mod +++ b/go.mod @@ -3,14 +3,17 @@ module uauth go 1.20 require ( - gitea.com/taozitaozi/gredis v0.0.0-20240131032054-b02845ce1e9d + gitea.com/loveuer/gredis v1.0.0 github.com/glebarez/sqlite v1.11.0 github.com/go-redis/redis/v8 v8.11.5 github.com/golang-jwt/jwt/v5 v5.2.1 github.com/google/uuid v1.6.0 github.com/hashicorp/golang-lru/v2 v2.0.7 + github.com/jackc/pgtype v1.14.4 github.com/jedib0t/go-pretty/v6 v6.6.1 github.com/loveuer/nf v0.2.12 + github.com/samber/lo v1.47.0 + github.com/spf13/cast v1.7.0 github.com/spf13/cobra v1.8.1 golang.org/x/crypto v0.26.0 golang.org/x/oauth2 v0.23.0 @@ -28,6 +31,7 @@ require ( github.com/go-sql-driver/mysql v1.7.0 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jackc/pgio v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect github.com/jackc/pgx/v5 v5.5.5 // indirect diff --git a/go.sum b/go.sum index 5a4b368..b904ca2 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,16 @@ +gitea.com/loveuer/gredis v1.0.0 h1:fbRS8YZObcp1KV1KGj8pDpIj1WrI0W8pwU9Ny/2fJys= +gitea.com/loveuer/gredis v1.0.0/go.mod h1:TQlubgDiyNTRXqASd/XIUrqPBLj9NZRR2DmV3V2ZyMY= gitea.com/taozitaozi/gredis v0.0.0-20240131032054-b02845ce1e9d h1:TpEOdRGqwzxx+DaN18nFE+g4EQYjneZOO1jcHtSon/g= gitea.com/taozitaozi/gredis v0.0.0-20240131032054-b02845ce1e9d/go.mod h1:QtcL846XUtSnhmW6TZAujUQ9V5jalY7frxzZOs00kFI= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= 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= @@ -12,32 +20,84 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo= github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k= github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw= github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0= +github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= +github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= +github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= +github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= +github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= +github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= +github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= +github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= +github.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w= +github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM= +github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= +github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= +github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= +github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= +github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A= +github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= +github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag= +github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= +github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= +github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= +github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= +github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= +github.com/jackc/pgtype v1.14.4 h1:fKuNiCumbKTAIxQwXfB/nsrnkEI6bPJrrSiMKgbJ2j8= +github.com/jackc/pgtype v1.14.4/go.mod h1:aKeozOde08iifGosdJpz9MBZonJOUJxqNpPBcMJTlVA= +github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= +github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= +github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= +github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= +github.com/jackc/pgx/v4 v4.18.2 h1:xVpYkNR5pk5bMCZGfClbO962UIqVABcAGt7ha1s/FeU= +github.com/jackc/pgx/v4 v4.18.2/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw= github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= +github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jedib0t/go-pretty/v6 v6.6.1 h1:iJ65Xjb680rHcikRj6DSIbzCex2huitmc7bDtxYVWyc= @@ -46,10 +106,29 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= +github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/loveuer/nf v0.2.12 h1:1Og+ORHsOWKFmy9kKJhjvXDkdbaurH82HjIxuGA3nNM= github.com/loveuer/nf v0.2.12/go.mod h1:M6reF17/kJBis30H4DxR5hrtgo/oJL4AV4cBe4HzJLw= +github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 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.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= @@ -59,6 +138,7 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= @@ -66,35 +146,153 @@ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94 github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= +github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc= +github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.9.2 h1:oxx1eChJGI6Uks2ZC4W1zpLlVgqB8ner4EuQwV4Ik1Y= github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= +github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +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.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 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= +github.com/stretchr/testify v1.8.1/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/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/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-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ= golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/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-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +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= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/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.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/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-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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= @@ -105,6 +303,7 @@ gorm.io/driver/postgres v1.5.9/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkw gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8= gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE= modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY= modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ= diff --git a/httptest/uauth/127.0.0.1 b/httptest/uauth/127.0.0.1 deleted file mode 100644 index e69de29..0000000 diff --git a/httptest/uauth/auth req.bru b/httptest/uauth/auth req.bru deleted file mode 100644 index 6ca096c..0000000 --- a/httptest/uauth/auth req.bru +++ /dev/null @@ -1,18 +0,0 @@ -meta { - name: auth req - type: http - seq: 2 -} - -get { - url: http://127.0.0.1:8080/authorize?client_id=12345&response_type=code&redirect_uri=http://localhost:8080/callback&scopde=balaba - body: none - auth: none -} - -params:query { - client_id: 12345 - response_type: code - redirect_uri: http://localhost:8080/callback - scopde: balaba -} diff --git a/httptest/uauth/bruno.json b/httptest/uauth/bruno.json deleted file mode 100644 index c46c40c..0000000 --- a/httptest/uauth/bruno.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "version": "1", - "name": "uauth", - "type": "collection", - "ignore": [ - "node_modules", - ".git" - ] -} \ No newline at end of file diff --git a/internal/client/client.go b/internal/client/client.go index e0a03bc..dbbaa6c 100644 --- a/internal/client/client.go +++ b/internal/client/client.go @@ -10,7 +10,7 @@ import ( "github.com/loveuer/nf/nft/resp" "golang.org/x/oauth2" "net/http" - "uauth/internal/tool" + "uauth/tool" ) //go:embed login.html diff --git a/internal/cmd/serve.go b/internal/cmd/serve.go index 7a5641a..f8ece14 100644 --- a/internal/cmd/serve.go +++ b/internal/cmd/serve.go @@ -4,10 +4,11 @@ import ( "github.com/spf13/cobra" "uauth/internal/opt" "uauth/internal/serve" - "uauth/internal/store/cache" - "uauth/internal/store/db" - "uauth/internal/tool" "uauth/model" + "uauth/pkg/cache" + "uauth/pkg/rbac" + "uauth/pkg/store" + "uauth/tool" ) func initServe() *cobra.Command { @@ -16,8 +17,9 @@ func initServe() *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { tool.TablePrinter(opt.Cfg) tool.Must(cache.Init(opt.Cfg.Svc.Cache)) - tool.Must(db.Init(cmd.Context(), opt.Cfg.Svc.DB)) - tool.Must(model.Init(db.Default.Session())) + tool.Must(store.Init(opt.Cfg.Svc.DB, store.Config{Debug: opt.Cfg.Debug})) + tool.Must(model.Init(store.Default.Session(tool.Timeout()))) + tool.Must(rbac.Init(store.Default, cache.Client)) return serve.Run(cmd.Context()) }, } diff --git a/internal/interfaces/enum.go b/internal/interfaces/enum.go deleted file mode 100644 index fd43fa6..0000000 --- a/internal/interfaces/enum.go +++ /dev/null @@ -1,11 +0,0 @@ -package interfaces - -type Enum interface { - Value() int64 - Code() string - Label() string - - MarshalJSON() ([]byte, error) - - All() []Enum -} diff --git a/internal/interfaces/logger.go b/internal/interfaces/logger.go deleted file mode 100644 index 8e75d58..0000000 --- a/internal/interfaces/logger.go +++ /dev/null @@ -1,7 +0,0 @@ -package interfaces - -type OpLogger interface { - Enum - Render(content map[string]any) (string, error) - Template() string -} diff --git a/internal/serve/handler/approve.go b/internal/serve/handler/approve.go index c356df3..0df8e12 100644 --- a/internal/serve/handler/approve.go +++ b/internal/serve/handler/approve.go @@ -12,9 +12,10 @@ import ( "net/http" "net/url" "time" - "uauth/internal/store/cache" - "uauth/internal/store/db" "uauth/model" + "uauth/pkg/cache" + "uauth/pkg/store" + "uauth/tool" ) //go:embed serve_approve.html @@ -51,7 +52,8 @@ func Approve(c *nf.Ctx) error { return c.Status(http.StatusBadRequest).SendString("Bad Request: invalid redirect uri") } - if err = db.Default.Session().Where("client_id", req.ClientId).Take(client).Error; err != nil { + if err = store.Default.Session(tool.TimeoutCtx(c.Context(), 3)). + Where("client_id", req.ClientId).Take(client).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return c.Status(http.StatusBadRequest).SendString("Bad Request: invalid client_id") } @@ -60,7 +62,8 @@ func Approve(c *nf.Ctx) error { return c.Status(http.StatusInternalServerError).SendString("Internal Server Error") } - db.Default.Session().Clauses(clause.OnConflict{DoNothing: true}). + store.Default.Session(tool.TimeoutCtx(c.Context(), 3)). + Clauses(clause.OnConflict{DoNothing: true}). Create(&model.AuthorizationRecord{ UserId: op.Id, ClientId: client.Id, diff --git a/internal/serve/handler/authorize.go b/internal/serve/handler/authorize.go index 593bab4..26a8230 100644 --- a/internal/serve/handler/authorize.go +++ b/internal/serve/handler/authorize.go @@ -11,9 +11,10 @@ import ( "net/http" "net/url" "time" - "uauth/internal/store/cache" - "uauth/internal/store/db" "uauth/model" + "uauth/pkg/cache" + "uauth/pkg/store" + "uauth/tool" ) var ( @@ -59,7 +60,8 @@ func Authorize(c *nf.Ctx) error { log.Info("[S] Authorize: username = %s, client_id = %s", op.Username, req.ClientId) - if err = db.Default.Session().Where("client_id", req.ClientId).Take(client).Error; err != nil { + if err = store.Default.Session(tool.TimeoutCtx(c.Context(), 3)). + Where("client_id", req.ClientId).Take(client).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return c.Status(http.StatusBadRequest).SendString("Bad Request: invalid client_id") } @@ -68,7 +70,8 @@ func Authorize(c *nf.Ctx) error { return c.Status(http.StatusInternalServerError).SendString("Internal Server Error") } - if err = db.Default.Session().Model(&model.AuthorizationRecord{}). + if err = store.Default.Session(tool.TimeoutCtx(c.Context(), 3)). + Model(&model.AuthorizationRecord{}). Where("user_id", op.Id). Where("client_id", client.Id). Take(authRecord). diff --git a/internal/serve/handler/login.go b/internal/serve/handler/login.go index 9e68da9..6a19aa2 100644 --- a/internal/serve/handler/login.go +++ b/internal/serve/handler/login.go @@ -9,10 +9,10 @@ import ( "gorm.io/gorm" "net/http" "time" - "uauth/internal/store/cache" - "uauth/internal/store/db" - "uauth/internal/tool" "uauth/model" + "uauth/pkg/cache" + "uauth/pkg/store" + "uauth/tool" ) var ( @@ -43,7 +43,8 @@ func LoginPage(c *nf.Ctx) error { return resp.Resp400(c, req) } - if err = db.Default.Session().Model(&model.Client{}). + if err = store.Default.Session(tool.TimeoutCtx(c.Context(), 3)). + Model(&model.Client{}). Where("client_id = ?", req.ClientId). Take(client). Error; err != nil { @@ -96,7 +97,8 @@ func LoginAction(c *nf.Ctx) error { return c.Status(http.StatusBadRequest).SendString("Bad Request: username, password is required") } - if err = db.Default.Session().Model(&model.User{}). + if err = store.Default.Session(tool.TimeoutCtx(c.Context(), 3)). + Model(&model.User{}). Where("username = ?", req.Username). Take(op).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { diff --git a/internal/serve/handler/registry.go b/internal/serve/handler/registry.go index 97cd406..613f9fe 100644 --- a/internal/serve/handler/registry.go +++ b/internal/serve/handler/registry.go @@ -6,9 +6,9 @@ import ( "github.com/loveuer/nf" "github.com/loveuer/nf/nft/resp" "gorm.io/gorm" - "uauth/internal/store/db" - "uauth/internal/tool" "uauth/model" + "uauth/pkg/store" + "uauth/tool" ) func ClientRegistry(c *nf.Ctx) error { @@ -36,7 +36,8 @@ func ClientRegistry(c *nf.Ctx) error { ClientSecret: Secret, } - if err = db.Default.Session().Create(platform).Error; err != nil { + if err = store.Default.Session(tool.TimeoutCtx(c.Context(), 3)). + Create(platform).Error; err != nil { if errors.Is(err, gorm.ErrDuplicatedKey) { return resp.Resp400(c, err, "当前平台已经存在") } @@ -83,7 +84,8 @@ func UserRegistryAction(c *nf.Ctx) error { Password: tool.NewPassword(req.Password), } - if err = db.Default.Session().Create(op).Error; err != nil { + if err = store.Default.Session(tool.TimeoutCtx(c.Context(), 3)). + Create(op).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return resp.Resp400(c, err, "用户名已存在") } diff --git a/internal/serve/handler/token.go b/internal/serve/handler/token.go index 044f557..8397921 100644 --- a/internal/serve/handler/token.go +++ b/internal/serve/handler/token.go @@ -9,10 +9,10 @@ import ( "gorm.io/gorm" "net/http" "strings" - "uauth/internal/store/cache" - "uauth/internal/store/db" - "uauth/internal/tool" "uauth/model" + "uauth/pkg/cache" + "uauth/pkg/store" + "uauth/tool" ) func verifyClient(c *nf.Ctx) (*model.Client, error) { @@ -46,7 +46,8 @@ func verifyClient(c *nf.Ctx) (*model.Client, error) { } clientId, clientSecret := strs[0], strs[1] - if err = db.Default.Session().Model(&model.Client{}). + if err = store.Default.Session(tool.TimeoutCtx(c.Context(), 3)). + Model(&model.Client{}). Where("client_id", clientId). Take(client). Error; err != nil { @@ -84,7 +85,8 @@ func HandleToken(c *nf.Ctx) error { username := c.Form("username") password := c.Form("password") - if err = db.Default.Session().Model(&model.User{}). + if err = store.Default.Session(tool.TimeoutCtx(c.Context(), 3)). + Model(&model.User{}). Where("username = ?", username). Take(op).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { @@ -118,7 +120,8 @@ func HandleToken(c *nf.Ctx) error { } op.Id = opId - if err = db.Default.Session().Take(op).Error; err != nil { + if err = store.Default.Session(tool.TimeoutCtx(c.Context(), 3)). + Take(op).Error; err != nil { log.Error("[S] handleToken: get op by id err, id = %d, err = %s", opId, err.Error()) return c.Status(http.StatusInternalServerError).SendString("Internal Server Error") } diff --git a/internal/serve/serve.go b/internal/serve/serve.go index cb3878c..23fa71c 100644 --- a/internal/serve/serve.go +++ b/internal/serve/serve.go @@ -4,10 +4,10 @@ import ( "context" "github.com/loveuer/nf" "github.com/loveuer/nf/nft/log" - "uauth/internal/middleware/auth" "uauth/internal/opt" "uauth/internal/serve/handler" - "uauth/internal/tool" + "uauth/pkg/middleware/auth" + "uauth/tool" ) func Run(ctx context.Context) error { diff --git a/internal/store/cache/client.go b/internal/store/cache/client.go deleted file mode 100644 index bd1b13f..0000000 --- a/internal/store/cache/client.go +++ /dev/null @@ -1,42 +0,0 @@ -package cache - -import ( - "encoding/json" - "uauth/internal/interfaces" -) - -const ( - Prefix = "sys:uauth:" -) - -var ( - Client interfaces.Cacher -) - -type encoded_value interface { - MarshalBinary() ([]byte, error) -} - -type decoded_value interface { - UnmarshalBinary(bs []byte) error -} - -func handleValue(value any) ([]byte, error) { - var ( - bs []byte - err error - ) - - switch value.(type) { - case []byte: - return value.([]byte), nil - } - - if imp, ok := value.(encoded_value); ok { - bs, err = imp.MarshalBinary() - } else { - bs, err = json.Marshal(value) - } - - return bs, err -} diff --git a/internal/store/db/db_test.go b/internal/store/db/db_test.go deleted file mode 100644 index 8cce6fa..0000000 --- a/internal/store/db/db_test.go +++ /dev/null @@ -1,9 +0,0 @@ -package db - -import ( - "testing" -) - -func TestOpen(t *testing.T) { - -} diff --git a/internal/tool/slice_test.go b/internal/tool/slice_test.go deleted file mode 100644 index 05b1676..0000000 --- a/internal/tool/slice_test.go +++ /dev/null @@ -1 +0,0 @@ -package tool diff --git a/model/init.go b/model/init.go index f86083c..866d8e6 100644 --- a/model/init.go +++ b/model/init.go @@ -3,7 +3,7 @@ package model import ( "gorm.io/gorm" "gorm.io/gorm/clause" - "uauth/internal/tool" + "uauth/tool" ) func Init(tx *gorm.DB) error { diff --git a/model/privilege.go b/model/privilege.go new file mode 100644 index 0000000..4e148b3 --- /dev/null +++ b/model/privilege.go @@ -0,0 +1,47 @@ +package model + +import ( + "fmt" + "strings" +) + +// platform:module:class:action +// admin:*:*:* +// admin:audit:*:* +// admin:audit:flow:* +// admin:audit:flow:operate + +type Privilege struct { + CreatedAt int64 `json:"created_at" gorm:"column:created_at;autoCreateTime:milli"` + UpdatedAt int64 `json:"updated_at" gorm:"column:updated_at;autoUpdateTime:milli"` + DeletedAt int64 `json:"deleted_at" gorm:"index;column:deleted_at;default:0"` + Code string `json:"code" gorm:"column:code;primaryKey"` + Label string `json:"label" gorm:"column:label"` + Parent string `json:"parent" gorm:"column:parent"` + Scope string `json:"scope" gorm:"column:scope"` +} + +func (p *Privilege) Validate() error { + ss := strings.Split(p.Code, ":") + + if len(ss) != 4 { + return fmt.Errorf("privilege must consist of four parts: (platform:module:class:action)") + } + + for _, s := range ss { + if len(s) == 0 { + return fmt.Errorf("privilege code parts must not be empty") + } + } + + code := strings.Clone(p.Code) + for strings.HasSuffix(code, ":*") { + code = code[:len(code)-2] + } + + if code != "*" && strings.Contains(code, "*") { + return fmt.Errorf("privilege can only have trailing wildcard search") + } + + return nil +} diff --git a/model/privilege_test.go b/model/privilege_test.go new file mode 100644 index 0000000..39fb9cf --- /dev/null +++ b/model/privilege_test.go @@ -0,0 +1,74 @@ +package model + +import ( + "testing" + "uauth/internal/util" +) + +func TestPrivilege_Validate(t *testing.T) { + type fields struct { + Code string + } + tests := []struct { + name string + fields fields + wantErr bool + }{ + { + name: "全匹配", + fields: fields{Code: "*:*:*:*"}, + wantErr: false, + }, + { + name: "部分全匹配1", + fields: fields{Code: "user:ok:*:*"}, + wantErr: false, + }, + { + name: "部分全匹配2", + fields: fields{Code: "user:ok:nice:*"}, + wantErr: false, + }, + { + name: "中间全匹配1", + fields: fields{Code: "user:*:*:ok"}, + wantErr: true, + }, + { + name: "中间全匹配2", + fields: fields{Code: "user:*:nice:*"}, + wantErr: true, + }, + { + name: "精确权限", + fields: fields{Code: "user:1:2:3"}, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &Privilege{ + Code: tt.fields.Code, + } + if err := p.Validate(); (err != nil) != tt.wantErr { + t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func BenchmarkPrivilegeValidate(b *testing.B) { + ps := make([]*Privilege, 100000) + + for i := 0; i < 100000; i++ { + ps[i] = &Privilege{ + Code: tool.RandomString(4) + ":" + tool.RandomString(5) + ":" + tool.RandomString(6) + ":" + tool.RandomString(7), + } + } + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + _ = ps[i%100000].Validate() + } +} diff --git a/model/role.go b/model/role.go new file mode 100644 index 0000000..3f48684 --- /dev/null +++ b/model/role.go @@ -0,0 +1,13 @@ +package model + +import "uauth/pkg/sqlType" + +type Role struct { + CreatedAt int64 `json:"created_at" gorm:"column:created_at;autoCreateTime:milli"` + UpdatedAt int64 `json:"updated_at" gorm:"column:updated_at;autoUpdateTime:milli"` + DeletedAt int64 `json:"deleted_at" gorm:"index;column:deleted_at;default:0"` + Code string `json:"code" gorm:"primaryKey;column:code"` + Label string `json:"label" gorm:"column:label"` + Parent string `json:"parent" gorm:"column:parent"` + PrivilegeCodes sqlType.StrSlice `json:"privilege_codes" gorm:"column:privilege_codes"` +} diff --git a/model/scope.go b/model/scope.go new file mode 100644 index 0000000..533bd3a --- /dev/null +++ b/model/scope.go @@ -0,0 +1,11 @@ +package model + +// 用户权限作用域 +type Scope struct { + Code string `json:"code" gorm:"column:code;type:varchar(8);not null;primaryKey"` + CreatedAt int64 `json:"created_at" gorm:"column:created_at;autoCreateTime:milli"` + UpdatedAt int64 `json:"updated_at" gorm:"column:updated_at;autoUpdateTime:milli"` + DeletedAt int64 `json:"deleted_at" gorm:"index;column:deleted_at;default:0"` + Label string `json:"label" gorm:"column:label;type:varchar(64)"` + Parent string `json:"parent" gorm:"column:parent;type:varchar(8)"` +} diff --git a/model/user.go b/model/user.go index 26716c6..328cc1c 100644 --- a/model/user.go +++ b/model/user.go @@ -6,9 +6,15 @@ import ( "github.com/loveuer/nf/nft/log" "time" "uauth/internal/opt" + "uauth/pkg/sqlType" ) -type Status int64 +type Status string + +const ( + StatusActive Status = "active" + StatusDisabled Status = "disabled" +) type User struct { Id uint64 `json:"id" gorm:"primaryKey;column:id"` @@ -19,7 +25,7 @@ type User struct { Username string `json:"username" gorm:"column:username;type:varchar(64);unique"` Password string `json:"-" gorm:"column:password;type:varchar(256)"` - Status Status `json:"status" gorm:"column:status;default:0"` + Status Status `json:"status" gorm:"column:status;default:''"` Nickname string `json:"nickname" gorm:"column:nickname;type:varchar(64)"` Avatar string `json:"avatar" gorm:"column:avatar;type:varchar(256)"` @@ -29,6 +35,9 @@ type User struct { CreatedByName string `json:"created_by_name" gorm:"column:created_by_name;type:varchar(64)"` LoginAt int64 `json:"login_at" gorm:"-"` + + Roles []*Role `json:"-" gorm:"-"` + RoleNames sqlType.StrSlice `json:"role_names" column:"role_names"` } func (u *User) JwtEncode() (token string, err error) { @@ -40,6 +49,7 @@ func (u *User) JwtEncode() (token string, err error) { "username": u.Username, "nickname": u.Nickname, "status": u.Status, + "avatar": u.Avatar, "login_at": now.UnixMilli(), }) diff --git a/internal/store/cache/cache_lru.go b/pkg/cache/cache_lru.go similarity index 89% rename from internal/store/cache/cache_lru.go rename to pkg/cache/cache_lru.go index 99665bf..85a548b 100644 --- a/internal/store/cache/cache_lru.go +++ b/pkg/cache/cache_lru.go @@ -5,10 +5,9 @@ import ( "github.com/hashicorp/golang-lru/v2/expirable" _ "github.com/hashicorp/golang-lru/v2/expirable" "time" - "uauth/internal/interfaces" ) -var _ interfaces.Cacher = (*_lru)(nil) +var _ Cache = (*_lru)(nil) type _lru struct { client *expirable.LRU[string, *_lru_value] @@ -38,7 +37,7 @@ func (l *_lru) Get(ctx context.Context, key string) ([]byte, error) { return v.bs, nil } -func (l *_lru) GetScan(ctx context.Context, key string) interfaces.Scanner { +func (l *_lru) GetScan(ctx context.Context, key string) Scanner { return newScanner(l.Get(ctx, key)) } @@ -68,7 +67,7 @@ func (l *_lru) GetEx(ctx context.Context, key string, duration time.Duration) ([ return v.bs, nil } -func (l *_lru) GetExScan(ctx context.Context, key string, duration time.Duration) interfaces.Scanner { +func (l *_lru) GetExScan(ctx context.Context, key string, duration time.Duration) Scanner { return newScanner(l.GetEx(ctx, key, duration)) } @@ -110,7 +109,7 @@ func (l *_lru) Del(ctx context.Context, keys ...string) error { return nil } -func newLRUCache() (interfaces.Cacher, error) { +func newLRUCache() (Cache, error) { client := expirable.NewLRU[string, *_lru_value](1024*1024, nil, 0) return &_lru{client: client}, nil diff --git a/internal/store/cache/cache_memory.go b/pkg/cache/cache_memory.go similarity index 86% rename from internal/store/cache/cache_memory.go rename to pkg/cache/cache_memory.go index fb9e508..500c353 100644 --- a/internal/store/cache/cache_memory.go +++ b/pkg/cache/cache_memory.go @@ -5,22 +5,21 @@ import ( "errors" "fmt" "time" - "uauth/internal/interfaces" - "gitea.com/taozitaozi/gredis" + "gitea.com/loveuer/gredis" ) -var _ interfaces.Cacher = (*_mem)(nil) +var _ Cache = (*_mem)(nil) type _mem struct { client *gredis.Gredis } -func (m *_mem) GetScan(ctx context.Context, key string) interfaces.Scanner { +func (m *_mem) GetScan(ctx context.Context, key string) Scanner { return newScanner(m.Get(ctx, key)) } -func (m *_mem) GetExScan(ctx context.Context, key string, duration time.Duration) interfaces.Scanner { +func (m *_mem) GetExScan(ctx context.Context, key string, duration time.Duration) Scanner { return newScanner(m.GetEx(ctx, key, duration)) } diff --git a/internal/store/cache/cache_redis.go b/pkg/cache/cache_redis.go similarity index 90% rename from internal/store/cache/cache_redis.go rename to pkg/cache/cache_redis.go index bbf7b5d..6c7dffe 100644 --- a/internal/store/cache/cache_redis.go +++ b/pkg/cache/cache_redis.go @@ -5,7 +5,6 @@ import ( "errors" "github.com/go-redis/redis/v8" "time" - "uauth/internal/interfaces" ) type _redis struct { @@ -25,7 +24,7 @@ func (r *_redis) Get(ctx context.Context, key string) ([]byte, error) { return []byte(result), nil } -func (r *_redis) GetScan(ctx context.Context, key string) interfaces.Scanner { +func (r *_redis) GetScan(ctx context.Context, key string) Scanner { return newScanner(r.Get(ctx, key)) } @@ -42,7 +41,7 @@ func (r *_redis) GetEx(ctx context.Context, key string, duration time.Duration) return []byte(result), nil } -func (r *_redis) GetExScan(ctx context.Context, key string, duration time.Duration) interfaces.Scanner { +func (r *_redis) GetExScan(ctx context.Context, key string, duration time.Duration) Scanner { return newScanner(r.GetEx(ctx, key, duration)) } diff --git a/internal/interfaces/database.go b/pkg/cache/client.go similarity index 56% rename from internal/interfaces/database.go rename to pkg/cache/client.go index af95046..eaa0250 100644 --- a/internal/interfaces/database.go +++ b/pkg/cache/client.go @@ -1,11 +1,17 @@ -package interfaces +package cache import ( "context" + "encoding/json" "time" ) -type Cacher interface { +const ( + // todo: config this + Prefix = "sys:uauth:" +) + +type Cache interface { Get(ctx context.Context, key string) ([]byte, error) GetScan(ctx context.Context, key string) Scanner GetEx(ctx context.Context, key string, duration time.Duration) ([]byte, error) @@ -20,3 +26,35 @@ type Cacher interface { type Scanner interface { Scan(model any) error } + +var ( + Client Cache +) + +type encoded_value interface { + MarshalBinary() ([]byte, error) +} + +type decoded_value interface { + UnmarshalBinary(bs []byte) error +} + +func handleValue(value any) ([]byte, error) { + var ( + bs []byte + err error + ) + + switch value.(type) { + case []byte: + return value.([]byte), nil + } + + if imp, ok := value.(encoded_value); ok { + bs, err = imp.MarshalBinary() + } else { + bs, err = json.Marshal(value) + } + + return bs, err +} diff --git a/internal/store/cache/error.go b/pkg/cache/error.go similarity index 100% rename from internal/store/cache/error.go rename to pkg/cache/error.go diff --git a/internal/store/cache/init.go b/pkg/cache/init.go similarity index 54% rename from internal/store/cache/init.go rename to pkg/cache/init.go index fdedc76..28f8080 100644 --- a/internal/store/cache/init.go +++ b/pkg/cache/init.go @@ -2,17 +2,22 @@ package cache import ( "fmt" - "gitea.com/taozitaozi/gredis" + "gitea.com/loveuer/gredis" "github.com/go-redis/redis/v8" "net/url" "strings" - "uauth/internal/tool" + "uauth/tool" ) -func Init(uri string) error { +func Init(uri string) (err error) { + Client, err = New(uri) + return err +} +func New(uri string) (Cache, error) { var ( - err error + client Cache + err error ) strs := strings.Split(uri, "::") @@ -20,10 +25,10 @@ func Init(uri string) error { switch strs[0] { case "memory": gc := gredis.NewGredis(1024 * 1024) - Client = &_mem{client: gc} + client = &_mem{client: gc} case "lru": - if Client, err = newLRUCache(); err != nil { - return err + if client, err = newLRUCache(); err != nil { + return nil, err } case "redis": var ( @@ -32,7 +37,7 @@ func Init(uri string) error { ) if len(strs) != 2 { - return fmt.Errorf("cache.Init: invalid cache uri: %s", uri) + return nil, fmt.Errorf("cache.Init: invalid cache uri: %s", uri) } uri := strs[1] @@ -42,7 +47,7 @@ func Init(uri string) error { } if ins, err = url.Parse(uri); err != nil { - return fmt.Errorf("cache.Init: url parse cache uri: %s, err: %s", uri, err.Error()) + return nil, fmt.Errorf("cache.Init: url parse cache uri: %s, err: %s", uri, err.Error()) } addr := ins.Host @@ -57,13 +62,13 @@ func Init(uri string) error { }) if err = rc.Ping(tool.Timeout(5)).Err(); err != nil { - return fmt.Errorf("cache.Init: redis ping err: %s", err.Error()) + return nil, fmt.Errorf("cache.Init: redis ping err: %s", err.Error()) } - Client = &_redis{client: rc} + client = &_redis{client: rc} default: - return fmt.Errorf("cache type %s not support", strs[0]) + return nil, fmt.Errorf("cache type %s not support", strs[0]) } - return nil + return client, nil } diff --git a/internal/store/cache/scan.go b/pkg/cache/scan.go similarity index 100% rename from internal/store/cache/scan.go rename to pkg/cache/scan.go diff --git a/internal/middleware/auth/auth.go b/pkg/middleware/auth/auth.go similarity index 97% rename from internal/middleware/auth/auth.go rename to pkg/middleware/auth/auth.go index 57c6d45..7ccb8d4 100644 --- a/internal/middleware/auth/auth.go +++ b/pkg/middleware/auth/auth.go @@ -7,9 +7,9 @@ import ( "github.com/loveuer/nf/nft/resp" "net/http" "time" - "uauth/internal/store/cache" - "uauth/internal/tool" "uauth/model" + "uauth/pkg/cache" + "uauth/tool" ) type Config struct { diff --git a/pkg/middleware/logger/logger.go b/pkg/middleware/logger/logger.go new file mode 100644 index 0000000..e848923 --- /dev/null +++ b/pkg/middleware/logger/logger.go @@ -0,0 +1,47 @@ +package logger + +import ( + "fmt" + "github.com/loveuer/nf" + "github.com/loveuer/nf/nft/log" + "github.com/loveuer/nf/nft/resp" + "strconv" + "time" + "uauth/tool" +) + +func New() nf.HandlerFunc { + + return func(c *nf.Ctx) error { + var ( + now = time.Now() + logFn func(msg string, data ...any) + ip = c.IP() + ) + + traceId := c.Context().Value(nf.TraceKey) + c.Locals(nf.TraceKey, traceId) + + err := c.Next() + + c.Writer.Header().Set(nf.TraceKey, fmt.Sprint(traceId)) + + status, _ := strconv.Atoi(c.Writer.Header().Get(resp.RealStatusHeader)) + duration := time.Since(now) + + msg := fmt.Sprintf("%s | %15s | %d[%3d] | %s | %6s | %s", traceId, ip, c.StatusCode, status, tool.HumanDuration(duration.Nanoseconds()), c.Method(), c.Path()) + + switch { + case status >= 500: + logFn = log.Error + case status >= 400: + logFn = log.Warn + default: + logFn = log.Info + } + + logFn(msg) + + return err + } +} diff --git a/pkg/rbac/create.go b/pkg/rbac/create.go new file mode 100644 index 0000000..3c66c2e --- /dev/null +++ b/pkg/rbac/create.go @@ -0,0 +1,110 @@ +package rbac + +import ( + "context" + "fmt" + "github.com/samber/lo" + "strings" + "uauth/model" +) + +func (u *RBAC) newScope(ctx context.Context, code, label, parent string) (*model.Scope, error) { + s := &model.Scope{Code: code, Label: label, Parent: parent} + if err := u.store.Session(ctx).Create(s).Error; err != nil { + return s, err + } + + return s, nil +} + +func (u *RBAC) GetScopeGroup(ctx context.Context, name string) (*model.Scope, error) { + scope := new(model.Scope) + err := u.store.Session(ctx).Where("name = ?", name).Take(scope).Error + + return scope, err +} + +func (u *RBAC) newRole(ctx context.Context, code, label, parent string, privileges ...*model.Privilege) (*model.Role, error) { + ps := lo.FilterMap( + privileges, + func(p *model.Privilege, _ int) (string, bool) { + if p == nil { + return "", false + } + + return p.Code, p.Code != "" + }, + ) + + r := &model.Role{ + Code: code, + Label: label, + Parent: parent, + PrivilegeCodes: ps, + } + + if err := u.store.Session(ctx).Create(r).Error; err != nil { + return r, err + } + + return r, nil +} + +func (u *RBAC) GetRole(ctx context.Context, name string) (*model.Role, error) { + var r model.Role + if err := u.store.Session(ctx).Take(&r, "name = ?", name).Error; err != nil { + return nil, err + } + + return &r, nil +} + +func (u *RBAC) newPrivilege(ctx context.Context, code, label string, parent string, scope string) (*model.Privilege, error) { + p := &model.Privilege{Code: code, Label: label, Parent: parent, Scope: scope} + + codes := strings.SplitN(code, ":", 4) + if len(codes) != 4 { + return nil, fmt.Errorf("invalid code format") + } + + wailcard := false + for _, item := range codes { + if item == "*" { + wailcard = true + } + + if wailcard && item != "*" { + return nil, fmt.Errorf("invalid code format") + } + + if len(item) > 8 { + return nil, fmt.Errorf("invalid code format: code snippet too long") + } + } + + if codes[0] != "*" { + if _, err := u.GetScopeGroup(ctx, codes[0]); err != nil { + return nil, err + } + } + + if err := u.store.Session(ctx).Create(p).Error; err != nil { + return p, err + } + + return p, nil +} + +func (u *RBAC) newUser(ctx context.Context, target *model.User) (*model.User, error) { + result := u.store.Session(ctx). + Create(target) + if result.Error != nil { + return nil, result.Error + } + + if result.RowsAffected != 1 { + return nil, fmt.Errorf("invalid rows affected") + } + + return target, nil +} diff --git a/pkg/rbac/rbac.go b/pkg/rbac/rbac.go new file mode 100644 index 0000000..bc6e38f --- /dev/null +++ b/pkg/rbac/rbac.go @@ -0,0 +1,83 @@ +package rbac + +import ( + "fmt" + "strings" + "uauth/model" + "uauth/pkg/cache" + "uauth/pkg/store" + "uauth/tool" +) + +type RBAC struct { + cache cache.Cache + store store.Store +} + +var ( + Default *RBAC +) + +func New(store store.Store, cache cache.Cache) (*RBAC, error) { + var ( + err error + u = &RBAC{ + store: store, + cache: cache, + } + rootPrivilege *model.Privilege + rootRole *model.Role + rootScope *model.Scope + rootUser *model.User + ) + + if err = u.store.Session(tool.Timeout()).AutoMigrate( + &model.Scope{}, + &model.Privilege{}, + &model.Role{}, + &model.User{}, + ); err != nil { + return nil, fmt.Errorf("urbac migrate err: %w", err) + } + + if rootPrivilege, err = u.newPrivilege(tool.Timeout(), "*:*:*:*", "admin", "", "*"); err != nil { + if !strings.Contains(strings.ToLower(err.Error()), "unique") { + return nil, err + } + } + + if rootRole, err = u.newRole(tool.Timeout(), "admin", "管理员", "", rootPrivilege); err != nil { + if !strings.Contains(strings.ToLower(err.Error()), "unique") { + return nil, err + } + } + + if rootScope, err = u.newScope(tool.Timeout(), "*", "全部", ""); err != nil { + if !strings.Contains(strings.ToLower(err.Error()), "unique") { + return nil, err + } + } + + rootUser = &model.User{ + Username: "admin", + Password: tool.NewPassword("123456"), + Status: model.StatusActive, + Nickname: "管理员", + RoleNames: []string{rootRole.Code}, + } + + if _, err = u.newUser(tool.Timeout(3), rootUser); err != nil { + if !strings.Contains(strings.ToLower(err.Error()), "unique") { + return nil, err + } + } + + _ = rootScope + + return u, nil +} + +func Init(store store.Store, cache cache.Cache) (err error) { + Default, err = New(store, cache) + return err +} diff --git a/pkg/sqlType/error.go b/pkg/sqlType/error.go new file mode 100644 index 0000000..d1195e0 --- /dev/null +++ b/pkg/sqlType/error.go @@ -0,0 +1,9 @@ +package sqlType + +import "errors" + +var ( + ErrConvertScanVal = errors.New("convert scan val to str err") + ErrInvalidScanVal = errors.New("scan val invalid") + ErrConvertVal = errors.New("convert err") +) diff --git a/pkg/sqlType/jsonb.go b/pkg/sqlType/jsonb.go new file mode 100644 index 0000000..ace1417 --- /dev/null +++ b/pkg/sqlType/jsonb.go @@ -0,0 +1,76 @@ +package sqlType + +import ( + "database/sql/driver" + "encoding/json" + + "github.com/jackc/pgtype" +) + +type JSONB struct { + Val pgtype.JSONB + Valid bool +} + +func NewJSONB(v interface{}) JSONB { + j := new(JSONB) + j.Val = pgtype.JSONB{} + if err := j.Val.Set(v); err == nil { + j.Valid = true + return *j + } + + return *j +} + +func (j *JSONB) Set(value interface{}) error { + if err := j.Val.Set(value); err != nil { + j.Valid = false + return err + } + + j.Valid = true + + return nil +} + +func (j *JSONB) Bind(model interface{}) error { + return j.Val.AssignTo(model) +} + +func (j *JSONB) Scan(value interface{}) error { + j.Val = pgtype.JSONB{} + if value == nil { + j.Valid = false + return nil + } + + j.Valid = true + + return j.Val.Scan(value) +} + +func (j JSONB) Value() (driver.Value, error) { + if j.Valid { + return j.Val.Value() + } + + return nil, nil +} + +func (j JSONB) MarshalJSON() ([]byte, error) { + if j.Valid { + return j.Val.MarshalJSON() + } + + return json.Marshal(nil) +} + +func (j *JSONB) UnmarshalJSON(b []byte) error { + if string(b) == "null" { + j.Valid = false + return j.Val.UnmarshalJSON(b) + } + + return j.Val.UnmarshalJSON(b) +} diff --git a/pkg/sqlType/num_slice.go b/pkg/sqlType/num_slice.go new file mode 100644 index 0000000..7bba191 --- /dev/null +++ b/pkg/sqlType/num_slice.go @@ -0,0 +1,71 @@ +package sqlType + +import ( + "database/sql/driver" + "fmt" + "strconv" + "strings" + + "github.com/spf13/cast" +) + +type NumSlice[T ~int | ~int64 | ~uint | ~uint64] []T + +func (n *NumSlice[T]) Scan(val interface{}) error { + str, ok := val.(string) + if !ok { + return ErrConvertScanVal + } + + length := len(str) + + if length <= 0 { + *n = make(NumSlice[T], 0) + return nil + } + + if str[0] != '{' || str[length-1] != '}' { + return ErrInvalidScanVal + } + + str = str[1 : length-1] + if len(str) == 0 { + *n = make(NumSlice[T], 0) + return nil + } + + numStrs := strings.Split(str, ",") + nums := make([]T, len(numStrs)) + + for idx := range numStrs { + num, err := cast.ToInt64E(strings.TrimSpace(numStrs[idx])) + if err != nil { + return fmt.Errorf("%w: can't convert to %T", ErrConvertVal, T(0)) + } + + nums[idx] = T(num) + } + + *n = nums + + return nil +} + +func (n NumSlice[T]) Value() (driver.Value, error) { + if n == nil { + return "{}", nil + } + + if len(n) == 0 { + return "{}", nil + } + + ss := make([]string, 0, len(n)) + for idx := range n { + ss = append(ss, strconv.Itoa(int(n[idx]))) + } + + s := strings.Join(ss, ", ") + + return fmt.Sprintf("{%s}", s), nil +} diff --git a/pkg/sqlType/string_slice.go b/pkg/sqlType/string_slice.go new file mode 100644 index 0000000..be3ccfa --- /dev/null +++ b/pkg/sqlType/string_slice.go @@ -0,0 +1,107 @@ +package sqlType + +import ( + "bytes" + "database/sql/driver" + "encoding/json" +) + +type StrSlice []string + +func (s *StrSlice) Scan(val interface{}) error { + + str, ok := val.(string) + if !ok { + return ErrConvertScanVal + } + + if len(str) < 2 { + return nil + } + + bs := make([]byte, 0, 128) + bss := make([]byte, 0, 2*len(str)) + + quoteCount := 0 + + for idx := 1; idx < len(str)-1; idx++ { + quote := str[idx] + switch quote { + case 44: + if quote == 44 && str[idx-1] != 92 && quoteCount == 0 { + if len(bs) > 0 { + if !(bs[0] == 34 && bs[len(bs)-1] == 34) { + bs = append([]byte{34}, bs...) + bs = append(bs, 34) + } + + bss = append(bss, bs...) + bss = append(bss, 44) + } + bs = bs[:0] + } else { + bs = append(bs, quote) + } + case 34: + if str[idx-1] != 92 { + quoteCount = (quoteCount + 1) % 2 + } + bs = append(bs, quote) + default: + bs = append(bs, quote) + } + + } + + if len(bs) > 0 { + if !(bs[0] == 34 && bs[len(bs)-1] == 34) { + bs = append([]byte{34}, bs...) + bs = append(bs, 34) + } + + bss = append(bss, bs...) + } else { + if len(bss) > 2 { + bss = bss[:len(bss)-2] + } + } + + bss = append([]byte{'['}, append(bss, ']')...) + + if err := json.Unmarshal(bss, s); err != nil { + return err + } + + return nil +} + +func (s StrSlice) Value() (driver.Value, error) { + if s == nil { + return "{}", nil + } + + if len(s) == 0 { + return "{}", nil + } + + buf := &bytes.Buffer{} + + encoder := json.NewEncoder(buf) + encoder.SetEscapeHTML(false) + + if err := encoder.Encode(s); err != nil { + return "{}", err + } + + bs := buf.Bytes() + + bs[0] = '{' + + if bs[len(bs)-1] == 10 { + bs = bs[:len(bs)-1] + } + + bs[len(bs)-1] = '}' + + return string(bs), nil +} diff --git a/internal/store/db/client.go b/pkg/store/client.go similarity index 63% rename from internal/store/db/client.go rename to pkg/store/client.go index 9759a23..72b230f 100644 --- a/internal/store/db/client.go +++ b/pkg/store/client.go @@ -1,13 +1,16 @@ -package db +package store import ( "context" - "uauth/internal/opt" - "uauth/internal/tool" + "uauth/tool" "gorm.io/gorm" ) +type Store interface { + Session(ctx context.Context) *gorm.DB +} + var ( Default *Client ) @@ -16,23 +19,21 @@ type Client struct { ctx context.Context cli *gorm.DB ttype string + debug bool } func (c *Client) Type() string { return c.ttype } -func (c *Client) Session(ctxs ...context.Context) *gorm.DB { - var ctx context.Context - if len(ctxs) > 0 && ctxs[0] != nil { - ctx = ctxs[0] - } else { +func (c *Client) Session(ctx context.Context) *gorm.DB { + if ctx == nil { ctx = tool.Timeout(30) } session := c.cli.Session(&gorm.Session{Context: ctx}) - if opt.Cfg.Debug { + if c.debug { session = session.Debug() } diff --git a/internal/store/db/init.go b/pkg/store/init.go similarity index 73% rename from internal/store/db/init.go rename to pkg/store/init.go index d137f13..dbf8299 100644 --- a/internal/store/db/init.go +++ b/pkg/store/init.go @@ -1,7 +1,6 @@ -package db +package store import ( - "context" "fmt" "strings" @@ -11,7 +10,11 @@ import ( "gorm.io/gorm" ) -func New(ctx context.Context, uri string) (*Client, error) { +type Config struct { + Debug bool +} + +func New(uri string, configs ...Config) (*Client, error) { strs := strings.Split(uri, "::") if len(strs) != 2 { @@ -20,6 +23,10 @@ func New(ctx context.Context, uri string) (*Client, error) { c := &Client{ttype: strs[0]} + if len(configs) > 0 && configs[0].Debug { + c.debug = true + } + var ( err error dsn = strs[1] @@ -43,8 +50,8 @@ func New(ctx context.Context, uri string) (*Client, error) { return c, nil } -func Init(ctx context.Context, uri string) (err error) { - if Default, err = New(ctx, uri); err != nil { +func Init(uri string, configs ...Config) (err error) { + if Default, err = New(uri, configs...); err != nil { return err } diff --git a/internal/tool/slice.go b/tool/bulk.go similarity index 100% rename from internal/tool/slice.go rename to tool/bulk.go diff --git a/internal/tool/cert.go b/tool/cert.go similarity index 100% rename from internal/tool/cert.go rename to tool/cert.go diff --git a/internal/tool/ctx.go b/tool/ctx.go similarity index 100% rename from internal/tool/ctx.go rename to tool/ctx.go diff --git a/internal/tool/file.go b/tool/file.go similarity index 100% rename from internal/tool/file.go rename to tool/file.go diff --git a/internal/tool/human.go b/tool/human.go similarity index 100% rename from internal/tool/human.go rename to tool/human.go diff --git a/internal/tool/must.go b/tool/must.go similarity index 100% rename from internal/tool/must.go rename to tool/must.go diff --git a/internal/tool/password.go b/tool/password.go similarity index 100% rename from internal/tool/password.go rename to tool/password.go diff --git a/internal/tool/password_test.go b/tool/password_test.go similarity index 100% rename from internal/tool/password_test.go rename to tool/password_test.go diff --git a/internal/tool/random.go b/tool/random.go similarity index 100% rename from internal/tool/random.go rename to tool/random.go diff --git a/internal/tool/table.go b/tool/table.go similarity index 100% rename from internal/tool/table.go rename to tool/table.go diff --git a/internal/tool/time.go b/tool/time.go similarity index 100% rename from internal/tool/time.go rename to tool/time.go