diff --git a/README.md b/README.md index 14c45c5..3f2bf42 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ -# GoAsali Framework -## About GoAsali + +

+ Flutter +

+
GoAsali is a Web Application framework whose purpose is to create Web Applications with a specific structure. @@ -12,4 +15,4 @@ Coming soon ... ⌛️🫠 Thank you for considering contributing to the GoAsali template. -You can by cloning the project and make a pull request to starting contributing. \ No newline at end of file +You can by cloning the project and make a pull request to starting contributing. diff --git a/go.mod b/go.mod index 8a3dde5..d943dfe 100644 --- a/go.mod +++ b/go.mod @@ -1,42 +1,48 @@ module github.com/abolfazlalz/goasali -go 1.18 +go 1.20 require ( github.com/caarlos0/env/v8 v8.0.0 - github.com/gin-gonic/gin v1.7.7 - github.com/go-playground/validator/v10 v10.13.0 + github.com/gin-gonic/gin v1.9.0 + github.com/go-playground/validator/v10 v10.11.2 + github.com/golang-jwt/jwt/v5 v5.0.0 github.com/joho/godotenv v1.5.1 github.com/nicksnyder/go-i18n/v2 v2.2.1 - golang.org/x/crypto v0.8.0 - golang.org/x/text v0.9.0 - gorm.io/driver/mysql v1.5.0 - gorm.io/driver/sqlite v1.5.0 - gorm.io/gorm v1.25.0 + github.com/redis/go-redis/v9 v9.0.5 + golang.org/x/crypto v0.11.0 + golang.org/x/text v0.11.0 + gorm.io/driver/mysql v1.5.1 + gorm.io/driver/sqlite v1.5.2 + gorm.io/gorm v1.25.2 ) require ( + github.com/bytedance/sonic v1.9.2 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-sql-driver/mysql v1.7.0 // indirect - github.com/golang-jwt/jwt/v5 v5.0.0 // indirect - github.com/golang/protobuf v1.5.2 // indirect + github.com/go-sql-driver/mysql v1.7.1 // indirect + github.com/goccy/go-json v0.10.2 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/kr/pretty v0.3.0 // indirect + github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/leodido/go-urn v1.2.4 // indirect - github.com/mattn/go-isatty v0.0.18 // indirect - github.com/mattn/go-sqlite3 v1.14.16 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mattn/go-sqlite3 v1.14.17 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/redis/go-redis/v9 v9.0.4 // indirect + github.com/pelletier/go-toml/v2 v2.0.8 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.11 // indirect - golang.org/x/sys v0.7.0 // indirect - google.golang.org/protobuf v1.26.0 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect - gopkg.in/yaml.v2 v2.3.0 // indirect + golang.org/x/arch v0.4.0 // indirect + golang.org/x/net v0.12.0 // indirect + golang.org/x/sys v0.10.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 60665fd..75eb309 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,17 @@ github.com/BurntSushi/toml v1.0.0 h1:dtDWrepsVPfW9H/4y7dDgFc2MBUSeJhlaDtK13CxFlU= github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/bsm/ginkgo/v2 v2.7.0 h1:ItPMPH90RbmZJt5GtkcNvIRuGEdwlBItdNVoyzaNQao= +github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y= +github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= +github.com/bytedance/sonic v1.9.2 h1:GDaNjuWSGu09guE9Oql0MSTNhNCLlWwO8y/xM5BzcbM= +github.com/bytedance/sonic v1.9.2/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= github.com/caarlos0/env/v8 v8.0.0 h1:POhxHhSpuxrLMIdvTGARuZqR4Jjm8AYmoi/JKlcScs0= github.com/caarlos0/env/v8 v8.0.0/go.mod h1:7K4wMY9bH0esiXSSHlfHLX5xKGQMnkH5Fk4TDSSSzfo= 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/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= 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,27 +19,23 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs= -github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8= +github.com/gin-gonic/gin v1.9.0/go.mod h1:W1Me9+hsUSyj3CePGrd1/QrKJMSJ1Tu/0hFEH89961k= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= -github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= -github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= -github.com/go-playground/validator/v10 v10.13.0 h1:cFRQdfaSMCOSfGCCLB20MHvuoHb/s5G8L5pu2ppK5AQ= -github.com/go-playground/validator/v10 v10.13.0/go.mod h1:dwu7+CG8/CtBiJFZDz4e+5Upb6OLw04gtBYw0mcG/z4= -github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= +github.com/go-playground/validator/v10 v10.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU= +github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= +github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -42,85 +45,81 @@ github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= +github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -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/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= -github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= -github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= -github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM= +github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/nicksnyder/go-i18n/v2 v2.2.1 h1:aOzRCdwsJuoExfZhoiXHy4bjruwCMdt5otbYojM/PaA= github.com/nicksnyder/go-i18n/v2 v2.2.1/go.mod h1:fF2++lPHlo+/kPaj3nB0uxtPwzlPm+BlgwGX7MkeGj0= +github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= +github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= 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/redis/go-redis/v9 v9.0.4 h1:FC82T+CHJ/Q/PdyLW++GeCO+Ol59Y4T7R4jbgjvktgc= -github.com/redis/go-redis/v9 v9.0.4/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk= -github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/redis/go-redis/v9 v9.0.5 h1:CuQcn5HIEeK7BgElubPP8CGtE0KakrnbBSTLjathl5o= +github.com/redis/go-redis/v9 v9.0.5/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk= +github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +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.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= -github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.4.0 h1:A8WCeEWhLwPBKNbFi5Wv5UTCBx5zzubnXDlMOFAzFMc= +golang.org/x/arch v0.4.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= -golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= +golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= +golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -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-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.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= +golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= 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/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-20200116001909-b77594299b42/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-20220722155257-8c9f86f7a55f/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.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/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.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= +golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 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.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= @@ -128,24 +127,21 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gorm.io/driver/mysql v1.5.0 h1:6hSAT5QcyIaty0jfnff0z0CLDjyRgZ8mlMHLqSt7uXM= -gorm.io/driver/mysql v1.5.0/go.mod h1:FFla/fJuCvyTi7rJQd27qlNX2v3L6deTR1GgTjSOLPo= -gorm.io/driver/sqlite v1.5.0 h1:zKYbzRCpBrT1bNijRnxLDJWPjVfImGEn0lSnUY5gZ+c= -gorm.io/driver/sqlite v1.5.0/go.mod h1:kDMDfntV9u/vuMmz8APHtHF0b4nyBB7sfCieC6G8k8I= -gorm.io/gorm v1.24.7-0.20230306060331-85eaf9eeda11/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= -gorm.io/gorm v1.25.0 h1:+KtYtb2roDz14EQe4bla8CbQlmb9dN3VejSai3lprfU= -gorm.io/gorm v1.25.0/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= +gorm.io/driver/mysql v1.5.1 h1:WUEH5VF9obL/lTtzjmML/5e6VfFR/788coz2uaVCAZw= +gorm.io/driver/mysql v1.5.1/go.mod h1:Jo3Xu7mMhCyj8dlrb3WoCaRd1FhsVh+yMXb1jUInf5o= +gorm.io/driver/sqlite v1.5.2 h1:TpQ+/dqCY4uCigCFyrfnrJnrW9zjpelWVoEVNy5qJkc= +gorm.io/driver/sqlite v1.5.2/go.mod h1:qxAuCol+2r6PannQDpOP1FP6ag3mKi4esLnB/jHed+4= +gorm.io/gorm v1.25.1/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= +gorm.io/gorm v1.25.2 h1:gs1o6Vsa+oVKG/a9ElL3XgyGfghFfkKA2SInQaCyMho= +gorm.io/gorm v1.25.2/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/http/users.http b/http/users.http index ff61d8e..244bf0e 100644 --- a/http/users.http +++ b/http/users.http @@ -17,7 +17,7 @@ Content-Type: application/json Accept-Language: fa { - "username": "abolfazlalz1", + "username": "abolfazlalz", "password": "helloworld" } @@ -25,6 +25,13 @@ Accept-Language: fa GET http://localhost:9000/auth Content-Type: application/json Accept-Language: fa +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFib2xmYXpsYWx6IiwiZXhwIjoxNjg2MTE5MzgwfQ.3UHg_e8BFcxfoW0CGWA7FCL6JIg_dQ98quXw8aF26xw + +### List of users +GET http://localhost:9000/admin/users +Content-Type: application/json +Accept-Language: fa +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFib2xmYXpsYWx6IiwiZXhwIjoxNjg0ODQ3NTEwfQ.kR9NQiEukHUUgMtNrbblNQkWVpmqwX30r8R-7oexyrw Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFib2xmYXpsYWx6IiwiZXhwIjoxNjg0MTU0MTc0fQ.z4f8mWwZQ90_EsR28TXLFSix6dHh5lFe7QCiurPIyAE ### Refresh token @@ -34,4 +41,4 @@ Accept-Language: fa { "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFib2xmYXpsYWx6MSIsImV4cCI6MTY4NzA3NzI3MX0.ojQn8JhSNmpq4htOf9Bbc2Rg_ObxFldsvQYdKrj8OpE" -} \ No newline at end of file +} diff --git a/internal/users/controllers/admin.go b/internal/users/controllers/admin.go new file mode 100644 index 0000000..d31e1a3 --- /dev/null +++ b/internal/users/controllers/admin.go @@ -0,0 +1,34 @@ +package controllers + +import ( + "fmt" + "github.com/abolfazlalz/goasali/internal/users/db/models" + "github.com/abolfazlalz/goasali/internal/users/services" + "github.com/abolfazlalz/goasali/pkg/cache" + "github.com/abolfazlalz/goasali/pkg/http/controllers" + "github.com/gin-gonic/gin" + "github.com/nicksnyder/go-i18n/v2/i18n" + "gorm.io/gorm" +) + +type AdminController struct { + *controllers.Controllers + service *services.AdminService +} + +func NewAdmin(db *gorm.DB, bundle *i18n.Bundle, cache cache.Cache) *AdminController { + return &AdminController{ + service: services.NewAdmin(db, cache), + Controllers: controllers.New(bundle, cache), + } +} + +func (ctrl AdminController) List(c *gin.Context) { + users := &[]models.User{} + if err := ctrl.service.UsersList(users); err != nil { + ctrl.HandleHttp(c, ctrl.ErrorMessage(err.Error())) + } + + fmt.Println(users) + c.JSON(200, users) +} diff --git a/internal/users/controllers/auth.go b/internal/users/controllers/auth.go index cf57da1..d73e072 100644 --- a/internal/users/controllers/auth.go +++ b/internal/users/controllers/auth.go @@ -27,8 +27,8 @@ type AuthController struct { authService services.AuthServiceI } -func NewAuthController(db *gorm.DB, bundle *i18n.Bundle, cache cache.Cache) IAuthController { - ctrl := controllers.NewController(bundle, cache) +func NewAuth(db *gorm.DB, bundle *i18n.Bundle, cache cache.Cache) IAuthController { + ctrl := controllers.New(bundle, cache) return &AuthController{ Controllers: ctrl, authService: services.NewAuthServiceLogs(services.NewAuthService(db, cache)), @@ -47,15 +47,15 @@ func (ac *AuthController) Login(c *gin.Context) { token, err := ac.authService.Login(user, body.Username, body.Password) if err != nil { if err == services.UserUnauthorizedError { - ac.HttpError.HandleHttp(c, ac.HttpCode(400), ac.I18nErrorMessageConfig(c, "authorization.unauthorized")) + ac.HttpError.HandleHttp(c, ac.HttpCode(400), ac.I18nErrorMessageConfig("authorization.unauthorized")) return } - ac.HttpError.HandleHttp(c, ac.HttpCode(500), ac.I18nErrorMessageConfig(c, "errors.internal_server")) + ac.HttpError.HandleHttp(c, ac.HttpCode(500), ac.I18nErrorMessageConfig("errors.internal_server")) return } c.JSON(200, gin.H{ - "user": user, + "service": user, "access_token": token.AccessToken, "refresh_token": token.RefreshToken, "status": true, @@ -71,7 +71,7 @@ func (ac *AuthController) CreateAccount(c *gin.Context) { } if body.Password != body.ConfirmPassword { - ac.HttpError.HandleHttp(c, ac.I18nErrorMessageConfig(c, "validation.password_same")) + ac.HttpError.HandleHttp(c, ac.I18nErrorMessageConfig("validation.password_same")) return } @@ -85,13 +85,13 @@ func (ac *AuthController) CreateAccount(c *gin.Context) { token, err := ac.authService.CreateAccount(&user) if err != nil { - log.Printf("Error during create new user: %v", err) - ac.HttpError.HandleHttp(c, ac.HttpCode(500), ac.I18nErrorMessageConfig(c, "errors.internal_server")) + log.Printf("Error during create new service: %v", err) + ac.HttpError.HandleHttp(c, ac.HttpCode(500), ac.I18nErrorMessageConfig("errors.internal_server")) return } c.JSON(201, gin.H{ - "user": user, + "service": user, "access_token": token.AccessToken, "refresh_token": token.RefreshToken, "status": true, @@ -118,9 +118,9 @@ func (ac *AuthController) RefreshToken(c *gin.Context) { } func (ac *AuthController) Info(c *gin.Context) { - user, err := c.Get("user") + user, err := c.Get("service") if !err { - ac.HttpError.HandleHttp(c, ac.HttpError.I18nErrorMessageConfig(c, "authorization.status_forbidden"), ac.HttpError.HttpCode(http.StatusForbidden)) + ac.HttpError.HandleHttp(c, ac.HttpError.I18nErrorMessageConfig("authorization.status_forbidden"), ac.HttpError.HttpCode(http.StatusForbidden)) return } c.JSON(http.StatusOK, user) diff --git a/internal/users/controllers/types.go b/internal/users/controllers/types.go index d28ff33..e74603a 100644 --- a/internal/users/controllers/types.go +++ b/internal/users/controllers/types.go @@ -6,7 +6,7 @@ type LoginUser struct { } type RegisterUser struct { - Username string `binding:"required,unique=users"` + Username string `binding:"required,unique=service"` Password string `binding:"required"` ConfirmPassword string `binding:"required" json:"confirm_password"` FirstName string `binding:"required" json:"first_name"` diff --git a/internal/users/db/models/roles.go b/internal/users/db/models/roles.go index e05dcb0..dfd80c7 100644 --- a/internal/users/db/models/roles.go +++ b/internal/users/db/models/roles.go @@ -6,20 +6,14 @@ import ( type Role struct { gorm.Model - Name string - RoleId uint - Role *Role `gorm:"constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"` + Name string + RoleId uint + Role *Role `gorm:"constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"` + Permissions []*Permission `gorm:"many2many:role_permissions;"` } type Permission struct { gorm.Model - Name string `json:"name" gorm:"unique"` -} - -type RolePermission struct { - gorm.Model - RoleId uint - PermissionId uint - Role Role - Permission Permission + Name string `json:"name" gorm:"unique"` + Roles []*Role `gorm:"many2many:role_permissions;"` } diff --git a/internal/users/db/repository/roles.go b/internal/users/db/repository/roles.go new file mode 100644 index 0000000..46aaad6 --- /dev/null +++ b/internal/users/db/repository/roles.go @@ -0,0 +1,48 @@ +package repository + +import ( + "errors" + "github.com/abolfazlalz/goasali/internal/users/db/models" + "github.com/abolfazlalz/goasali/pkg/cache" + "github.com/abolfazlalz/goasali/pkg/repositories" + "gorm.io/gorm" +) + +var ( + RoleNotFound = errors.New("role not found ") +) + +type Role struct { + *repositories.Repository[models.Role] +} + +func NewRoleRepository(db *gorm.DB, cache cache.Cache) *Role { + return &Role{ + Repository: repositories.NewRepositoryInstance[models.Role](db, cache), + } +} + +func (r *Role) AssignPermissions(roleId uint, permissions ...models.Permission) error { + var role *models.Role + r.Db.First(role, "id=?", roleId) + if role == nil { + return RoleNotFound + } + if err := r.Db.Where("id=?", roleId).Association("Permission").Append(permissions); err != nil { + return err + } + return nil +} + +func (r *Role) CreatePermission(permission *models.Permission) error { + re := r.Db.Where("name=?", permission.Name).FirstOrCreate(permission) + return re.Error +} + +func (r *Role) DeletePermissionById(id ...uint) error { + if len(id) < 1 { + return errors.New("one id for delete a permission required") + } + r.Db.Delete(id) + return nil +} diff --git a/internal/users/middlewares/auth.go b/internal/users/middlewares/auth.go index 0b23481..bc77599 100644 --- a/internal/users/middlewares/auth.go +++ b/internal/users/middlewares/auth.go @@ -5,14 +5,50 @@ import ( "github.com/abolfazlalz/goasali/internal/users/db/models" "github.com/abolfazlalz/goasali/internal/users/db/repository" "github.com/abolfazlalz/goasali/internal/users/utils/tokens" + "github.com/abolfazlalz/goasali/pkg/cache" "github.com/abolfazlalz/goasali/pkg/errors" routes "github.com/abolfazlalz/goasali/pkg/http/routers" "github.com/gin-gonic/gin" "log" "net/http" "strings" + "time" ) +func IsAuthMiddlewareCache(c *gin.Context) { + context := routes.NewContext(c) + hErr := errors.NewByContext(c) + + cacheMng := context.Cache + + bearToken := c.GetHeader("Authorization") + + token := strings.Split(bearToken, " ")[1] + key := fmt.Sprintf("jwt_key_%s", bearToken) + + var user *models.User + if err := cacheMng.Get(key, user); err != nil { + panic(err) + } + if user == nil { + userJwt := tokens.NewUserJwt(token, context.DB, cacheMng) + if err := userJwt.User(user); err != nil { + panic(err) + } + if err := cacheMng.Set(cache.Item{Key: key, Value: user, TTL: 60 * time.Second}); err != nil { + panic(err) + } + } + + if user != nil { + c.Set("service", &user) + c.Next() + return + } + + hErr.HandleHttp(c, hErr.I18nErrorMessageConfig("authorization.access_denied")) +} + func IsAuthMiddleware(c *gin.Context) { prefix := log.Prefix() @@ -40,7 +76,7 @@ func IsAuthMiddleware(c *gin.Context) { log.Printf("Error in check middleware auth: %v", err) if verr, ok := err.(errors.I18nMessageError); ok { errors.NewByContext(c).I18nErrorMessage(c, verr.I18nId) - httpErr.HandleHttp(c, httpErr.HttpCode(http.StatusUnauthorized), httpErr.I18nErrorMessageConfig(c, verr.I18nId)) + httpErr.HandleHttp(c, httpErr.HttpCode(http.StatusUnauthorized), httpErr.I18nErrorMessageConfig(verr.I18nId)) return } @@ -55,7 +91,6 @@ func IsAuthMiddleware(c *gin.Context) { return } - c.Set("user", &user) - + c.Set("service", &user) c.Next() } diff --git a/internal/users/routers/routers.go b/internal/users/routers/routers.go index ec7d920..1eb80b3 100644 --- a/internal/users/routers/routers.go +++ b/internal/users/routers/routers.go @@ -11,15 +11,27 @@ type UserRouter struct { } func NewUserRoute() *UserRouter { - return &UserRouter{routes.NewRouteModule("users")} + return &UserRouter{routes.NewRouteModule("service")} } -func (UserRouter) Listen(route *routes.RouteModuleParams) { - ctrl := controllers.NewAuthController(route.DB, route.Bundle, route.Cache) +func (UserRouter) adminCtrl(route *routes.RouteModuleParams) { + ctrl := controllers.NewAdmin(route.DB, route.Bundle, route.Cache) + grp := route.Router.Group("/admin") + grp.GET("/service", ctrl.List) +} + +func (UserRouter) authCtrl(route *routes.RouteModuleParams) { + ctrl := controllers.NewAuth(route.DB, route.Bundle, route.Cache) ctrl = controllers.NewAuthLogs(ctrl) + grp := route.Router.Group("/auth") grp.POST("/register", ctrl.CreateAccount) grp.POST("/login", ctrl.Login) grp.POST("/refresh", ctrl.RefreshToken) - grp.Use(middlewares.IsAuthMiddleware).GET("/", ctrl.Info) + grp.Use(middlewares.IsAuthMiddlewareCache).GET("/", ctrl.Info) +} + +func (ur UserRouter) Listen(route *routes.RouteModuleParams) { + ur.authCtrl(route) + ur.adminCtrl(route) } diff --git a/internal/users/services/admin.go b/internal/users/services/admin.go new file mode 100644 index 0000000..63e7252 --- /dev/null +++ b/internal/users/services/admin.go @@ -0,0 +1,42 @@ +package services + +import ( + "github.com/abolfazlalz/goasali/internal/users/db/models" + "github.com/abolfazlalz/goasali/internal/users/db/repository" + "github.com/abolfazlalz/goasali/pkg/cache" + "gorm.io/gorm" +) + +type AdminInterface interface { + UsersList(users *[]models.User) error + CreateAdmin(user *models.User) error +} + +type AdminService struct { + AdminInterface + repo *repository.UserRepository + role *Role + db *gorm.DB +} + +func NewAdmin(db *gorm.DB, cache cache.Cache) *AdminService { + return &AdminService{ + repo: repository.NewUserRepository(db, cache), + role: NewRole(db, cache), + } +} + +func (service *AdminService) UsersList(users *[]models.User) error { + return service.repo.List(users).Error +} + +func (service *AdminService) CreateAdmin(user *models.User) error { + tx := service.repo.Create(user) + return tx.Error +} + +func (service *AdminService) CreateSuperAdmin(user *models.User) error { + role := models.Role{Name: "Admin"} + //permission + return nil +} diff --git a/internal/users/services/roles.go b/internal/users/services/roles.go new file mode 100644 index 0000000..3936f77 --- /dev/null +++ b/internal/users/services/roles.go @@ -0,0 +1,59 @@ +package services + +import ( + "errors" + "github.com/abolfazlalz/goasali/internal/users/db/models" + "github.com/abolfazlalz/goasali/internal/users/db/repository" + "github.com/abolfazlalz/goasali/pkg/cache" + "gorm.io/gorm" +) + +type RoleServiceI interface { + CreateRole(*models.Role) error + AssignRole(userId int, roleId int) error +} + +type Role struct { + RoleServiceI + repo *repository.Role +} + +func NewRole(db *gorm.DB, cache cache.Cache) *Role { + return &Role{ + repo: repository.NewRoleRepository(db, cache), + } +} + +func (r *Role) CreateRole(role *models.Role) error { + re := r.repo.Create(role) + return re.Error +} + +func (r *Role) CreateRoleIfNotExists(role *models.Role, permissions ...models.Permission) error { + if re := r.repo.FirstOrCreate(role); re.Error() != "" { + return errors.New(re.Error()) + } + if err := r.repo.AssignPermissions(role.RoleId, permissions...); err != nil { + return err + } + return nil +} + +func (r *Role) AssignRole(userId int, roleId int) error { + return nil +} + +func (r *Role) CreatePermission(permissions ...*models.Permission) error { + var id []uint + var err error + for _, permission := range permissions { + err = r.repo.CreatePermission(permission) + id = append(id, permission.ID) + } + + if err != nil { + r.repo.Delete(id...) + return err + } + return nil +} diff --git a/internal/users/utils/tokens/users.go b/internal/users/utils/tokens/users.go new file mode 100644 index 0000000..9d47166 --- /dev/null +++ b/internal/users/utils/tokens/users.go @@ -0,0 +1,30 @@ +package tokens + +import ( + "github.com/abolfazlalz/goasali/internal/users/db/models" + "github.com/abolfazlalz/goasali/internal/users/db/repository" + "github.com/abolfazlalz/goasali/pkg/cache" + "gorm.io/gorm" +) + +type UserJwt struct { + token string + db *gorm.DB + cache cache.Cache +} + +func NewUserJwt(token string, db *gorm.DB, cache cache.Cache) *UserJwt { + return &UserJwt{token, db, cache} +} + +func (uj *UserJwt) User(user *models.User) error { + token := New() + claim, err := token.ValidateJwtToken(uj.token) + if err != nil { + return err + } + + repo := repository.NewUserRepository(uj.db, uj.cache) + + return repo.FindByUsername(claim.Username, user) +} diff --git a/languages/en/authorization.json b/languages/en/authorization.json index 4bb03e8..91a8a43 100644 --- a/languages/en/authorization.json +++ b/languages/en/authorization.json @@ -1,5 +1,6 @@ { "unauthorized": "Email or password is incorrect.", "invalid_bearer_token": "the token format entered is invalid", - "status_forbidden": "You do not have access to this section" + "status_forbidden": "You do not have access to this section", + "access_denied": "Access denied" } \ No newline at end of file diff --git a/languages/fa/authorization.json b/languages/fa/authorization.json index 78f2960..c45a627 100644 --- a/languages/fa/authorization.json +++ b/languages/fa/authorization.json @@ -1,5 +1,6 @@ { "unauthorized": "نام کاربری یا رمزعبور نادرست می باشد.", "invalid_bearer_token": "توکن وارد شده نامعتبر می باشد.", - "status_forbidden": "شما به این بخش دسترسی ندارید." + "status_forbidden": "شما به این بخش دسترسی ندارید.", + "access_denied": "دسترسی محدود شده است." } \ No newline at end of file diff --git a/main.go b/main.go index de6ba72..a7b12c2 100644 --- a/main.go +++ b/main.go @@ -3,7 +3,9 @@ package main import ( "context" "github.com/abolfazlalz/goasali/internal/users/db/migrate" + "github.com/abolfazlalz/goasali/internal/users/db/models" "github.com/abolfazlalz/goasali/internal/users/routers" + "github.com/abolfazlalz/goasali/internal/users/services" "github.com/abolfazlalz/goasali/pkg/cache" "github.com/abolfazlalz/goasali/pkg/config" "github.com/abolfazlalz/goasali/pkg/database" @@ -29,11 +31,17 @@ func main() { log.Fatalf("Error in loading `.env` file: %v", err) } - databaseConfig, err := database.LoadDatabase() - cache, err := cache.New(ctx) - if err != nil { - log.Fatalf("Error during loading cache: %v", err) - return + var err error + var databaseConfig *database.Database + if databaseConfig, err = database.LoadDatabase(); err != nil { + log.Fatalf("Error during loading database: %v", err) + } + var cacheInstance cache.Cache + if cacheInstance, err = cache.New(ctx); err != nil { + if err != nil { + log.Fatalf("Error during loading cache: %v", err) + return + } } // Migrate modules models @@ -41,14 +49,18 @@ func main() { log.Fatalf("Database migration failed %v", err) } + db := databaseConfig.DB + //admin := services.NewAdmin(db, cacheInstance) + role := services.NewRole(db, cacheInstance) + err = role.CreatePermission(&models.Permission{Name: "Read"}, &models.Permission{Name: "Create"}) if err != nil { - log.Fatalf("Can't loading database: %v", err) + log.Printf("Error during create permissions: %v", err) } - router := routes.SetupRouter(databaseConfig.DB, m.Bundle, cache) + router := routes.SetupRouter(databaseConfig.DB, m.Bundle, cacheInstance) router.AddRoutes(routers.NewUserRoute()) if err := router.Listen(); err != nil { - log.Fatalf("Error in listenning gin http server: %v", err) + log.Fatalf("Error in listenning gin service server: %v", err) } } diff --git a/pkg/cache/cache.go b/pkg/cache/cache.go index 50eebde..a814dcf 100644 --- a/pkg/cache/cache.go +++ b/pkg/cache/cache.go @@ -4,16 +4,23 @@ import ( "context" "github.com/abolfazlalz/goasali/pkg/config" "log" + "time" ) type SupportValues interface { string | int } +type Item struct { + Key string + Value interface{} + TTL time.Duration +} + type Cache interface { - Get(key string) (interface{}, error) - Set(key string, value interface{}, ttl uint) error - Remember(key string, ttl uint, f func() interface{}) (interface{}, error) + Get(key string, value interface{}) error + Set(item Item) error + Remember(key string, ttl time.Duration, result interface{}, f func(result interface{})) error Forget(key ...string) error } @@ -31,8 +38,20 @@ func New(ctx context.Context) (Cache, error) { } if configCache.Type == "redis" { log.Print("Starting redis as cache") - return NewRedis(ctx), nil + return NewRedis(WithContext(ctx)), nil } return nil, InvalidTypeError{Type: configCache.Type} } + +func (item *Item) ttl() time.Duration { + ttl := time.Hour + if item.TTL == 0 { + return ttl + } + if item.TTL < time.Second { + log.Printf("Too short ttl for key: %q: %s", item.Key, item.TTL) + return ttl + } + return ttl +} diff --git a/pkg/cache/redis.go b/pkg/cache/redis.go index 3f3ecb1..4f03925 100644 --- a/pkg/cache/redis.go +++ b/pkg/cache/redis.go @@ -2,7 +2,7 @@ package cache import ( "context" - "fmt" + "encoding/json" "github.com/abolfazlalz/goasali/pkg/config" "github.com/redis/go-redis/v9" "log" @@ -11,13 +11,37 @@ import ( type Redis struct { Cache - client *redis.Client - ctx context.Context + client *redis.Client + ctx context.Context + defaultTTL time.Duration } -func NewRedis(ctx context.Context) *Redis { +type RedisOptionFunc func(*redisOptions) + +type redisOptions struct { + ctx context.Context +} + +func defaultRedisOption() *redisOptions { + return &redisOptions{ + ctx: context.Background(), + } +} + +func WithContext(ctx context.Context) RedisOptionFunc { + return func(options *redisOptions) { + options.ctx = ctx + } +} + +func NewRedis(optionsFunc ...RedisOptionFunc) *Redis { redisConfig, err := config.LoadCache() + options := defaultRedisOption() + for _, fn := range optionsFunc { + fn(options) + } + if err != nil { log.Fatal("Error in load cache redisConfig: ", redisConfig) } @@ -27,55 +51,34 @@ func NewRedis(ctx context.Context) *Redis { Password: redisConfig.Redis.Password, DB: 0, }) - return &Redis{client: rdb, ctx: ctx} + return &Redis{client: rdb, ctx: options.ctx, defaultTTL: time.Hour} } -func (r Redis) Set(key string, value interface{}, ttl uint) error { - du, err := time.ParseDuration(fmt.Sprintf("%ds", ttl)) +func (r Redis) Set(item Item) error { + data, err := json.Marshal(item.Value) if err != nil { return err } - re := r.client.Set(r.ctx, key, value, du) + re := r.client.Set(r.ctx, item.Key, data, item.ttl()) if err := re.Err(); err != nil { return err } return nil } -func (r Redis) Get(key string) (interface{}, error) { - re, err := r.client.Get(r.ctx, key).Result() +func (r Redis) Get(key string, result interface{}) error { + re, err := r.client.Get(r.ctx, key).Bytes() if err == redis.Nil { - return "", nil + return nil } if err != nil { - return "", err - } - return re, nil -} - -// Remember If a key has value return else, set value from function callback -func (r Redis) Remember(key string, ttl uint, f func() interface{}) (interface{}, error) { - var err error - var result interface{} - - result, err = r.Get(key) - - if err != nil { - return "", err - } - - // Still this key has value in cache - if result != "" { - return result, nil + return err } - - result = f() - - if err := r.Set(key, result, ttl); err != nil { - return "", err + log.Println(string(re)) + if err := json.Unmarshal(re, result); err != nil { + return err } - - return result, nil + return nil } func (r Redis) Forget(key ...string) error { diff --git a/pkg/cache/redis_test.go b/pkg/cache/redis_test.go new file mode 100644 index 0000000..0ead94e --- /dev/null +++ b/pkg/cache/redis_test.go @@ -0,0 +1,83 @@ +package cache + +import ( + "fmt" + "log" + "testing" + "time" +) + +type User struct { + Name string `json:"name"` + Lastname string `json:"lastname"` +} + +var cache *Redis + +func TestInit(t *testing.T) { + cache = NewRedis() +} + +func TestRedis_Set(t *testing.T) { + err := cache.Set(Item{"test", "value", time.Second * 1}) + if err != nil { + t.Fatal(err) + } + + var value string + if err := cache.Get("test", &value); err != nil { + if err != nil { + t.Fatal(err) + return + } + t.Fatalf("value must be 'value' instead is `%s`", value) + } + + TestRedis_Set2(t) +} + +func TestRedis_Forget(t *testing.T) { + fmt.Println("T") + + value := "value" + key := "test" + if err := cache.Set(Item{key, value, time.Second * 1}); err != nil { + t.Fatal(err) + } + if err := cache.Forget(key); err != nil { + t.Fatal(err) + } + + var result string + if err := cache.Get(key, &result); err != nil { + if err != nil { + t.Fatal(err) + } + } + if result != "" { + t.Fatal("Try to forget but still exists") + } +} + +func TestRedis_Set2(t *testing.T) { + log.Println("fuck") + key := "struct_cache" + value := User{Name: "Abolfazl", Lastname: "Alizadeh"} + if err := cache.Set(Item{key, &value, time.Second * 1}); err != nil { + log.Fatal(err) + } + + var u User + //var u map[string]string + + err := cache.Get(key, &u) + + if err != nil { + t.Fatal(err) + } + + if u.Lastname != value.Lastname || u.Name != value.Name { + log.Fatal("Name and Lastname User in cache does not match") + } + +} diff --git a/pkg/errors/errors.go b/pkg/errors/errors.go index c201ce8..f041b6c 100644 --- a/pkg/errors/errors.go +++ b/pkg/errors/errors.go @@ -13,6 +13,7 @@ type HttpErrorMessage interface { } type httpErrorConfig struct { + c *gin.Context httpCode int message string } @@ -40,9 +41,10 @@ func (he *HttpError) mustLocalize(c *gin.Context, lc *i18n.LocalizeConfig) strin return he.getLocalizer(c).MustLocalize(lc) } -func defaultConfig() *httpErrorConfig { +func defaultConfig(c *gin.Context) *httpErrorConfig { return &httpErrorConfig{ httpCode: 500, message: "Unknown server error", + c: c, } } @@ -58,9 +60,9 @@ func (he *HttpError) ErrorMessage(message string) OptionFunc { } } -func (he *HttpError) I18nErrorMessageConfig(c *gin.Context, id string) OptionFunc { +func (he *HttpError) I18nErrorMessageConfig(id string) OptionFunc { return func(opt *httpErrorConfig) { - opt.message = he.I18nErrorMessage(c, id) + opt.message = he.I18nErrorMessage(opt.c, id) } } @@ -80,7 +82,7 @@ func (he *HttpError) I18nErrorMessageOrDefault(c *gin.Context, id string, defVal } func (he *HttpError) HandleHttp(c *gin.Context, configs ...OptionFunc) { - config := defaultConfig() + config := defaultConfig(c) for _, cf := range configs { cf(config) @@ -89,5 +91,5 @@ func (he *HttpError) HandleHttp(c *gin.Context, configs ...OptionFunc) { message := config.message response := gin.H{"message": message, "status": false} - c.JSON(config.httpCode, response) + c.AbortWithStatusJSON(config.httpCode, response) } diff --git a/pkg/errors/gin.go b/pkg/errors/gin.go index 922f69b..6e7d382 100644 --- a/pkg/errors/gin.go +++ b/pkg/errors/gin.go @@ -52,7 +52,7 @@ func (he *HttpError) HandleGinError(err error, c *gin.Context) { } if verr, ok := err.(I18nMessageError); ok { - he.HandleHttp(c, he.I18nErrorMessageConfig(c, verr.I18nId)) + he.HandleHttp(c, he.I18nErrorMessageConfig(verr.I18nId)) return } diff --git a/pkg/http/controllers/controllers.go b/pkg/http/controllers/controllers.go index 4499095..5357de0 100644 --- a/pkg/http/controllers/controllers.go +++ b/pkg/http/controllers/controllers.go @@ -13,7 +13,7 @@ type Controllers struct { Cache cache.Cache } -func NewController(bundle *i18n.Bundle, cache cache.Cache) *Controllers { +func New(bundle *i18n.Bundle, cache cache.Cache) *Controllers { return &Controllers{ Bundle: bundle, HttpError: &errors.HttpError{Bundle: bundle}, diff --git a/pkg/repositories/cache.go b/pkg/repositories/cache.go index e4d81eb..169927f 100644 --- a/pkg/repositories/cache.go +++ b/pkg/repositories/cache.go @@ -67,13 +67,10 @@ func NewCache[T any](repo *Repository[T], fns ...CacheConfigFn) *Cache[T] { func (c Cache[T]) Get(id uint) *T { key := fmt.Sprintf("%s-get-%d", c.prefix, id) - value, _ := c.Cache.Remember(key, 100, func() interface{} { - return c.Repository.Get(id) + var result T + _ = c.Cache.Remember(key, 100, &result, func(x interface{}) { + x = c.Repository.Get(id) }) - model, ok := value.(T) - if !ok { - return nil - } - return &model + return &result } diff --git a/pkg/repositories/list_query.go b/pkg/repositories/list_query.go new file mode 100644 index 0000000..a5ead12 --- /dev/null +++ b/pkg/repositories/list_query.go @@ -0,0 +1,48 @@ +package repositories + +type Operator string + +var ( + OrOperator Operator = "or" + AndOperator Operator = "and" +) + +type ListQueryOption struct { + operator Operator + conditions []interface{} + limit int + offset int +} + +type ListQueryExecuteFn func(option *ListQueryOption) + +func defaultListQuery() *ListQueryOption { + return &ListQueryOption{ + operator: AndOperator, + limit: 10, + offset: 0, + } +} + +// WithOperator List query with a dedicated operator [Or, and] +func WithOperator(op Operator) ListQueryExecuteFn { + return func(option *ListQueryOption) { + option.operator = op + } +} + +// WithLimit Set limit for query +// Ex: select * from service limit 10 +func WithLimit(limit int) ListQueryExecuteFn { + return func(option *ListQueryOption) { + option.limit = limit + } +} + +// WithOffset Set offset sql query +// Ex: select * from service offset 10 +func WithOffset(offset int) ListQueryExecuteFn { + return func(option *ListQueryOption) { + option.offset = offset + } +} diff --git a/pkg/repositories/repositories.go b/pkg/repositories/repositories.go index 3db33a8..f9087c4 100644 --- a/pkg/repositories/repositories.go +++ b/pkg/repositories/repositories.go @@ -7,8 +7,13 @@ import ( type Interface[T any] interface { Create(model *T) (tx *gorm.DB) + FirstOrCreate(model *T) CreateMap(model map[string]string) (tx *gorm.DB) Get(id uint) *T + Update(id uint, model map[string]string) (tx *gorm.DB) + Delete(id ...uint) (tx *gorm.DB) + // List get list of dedicated models with a condition + List(models *[]T, condition ...T) (tx *gorm.DB) } type Repository[T any] struct { @@ -23,3 +28,28 @@ func NewRepositoryInstance[T any](db *gorm.DB, cache cache.Cache) *Repository[T] Cache: cache, } } + +func (r Repository[T]) List(models *[]T, queryExecute ...ListQueryExecuteFn) (tx *gorm.DB) { + query := defaultListQuery() + for _, fn := range queryExecute { + fn(query) + } + + tx = r.Db.Limit(query.limit) + tx = tx.Offset(query.offset) + + for _, condition := range query.conditions { + tx = tx.Where(condition) + } + + tx.Find(models) + + return +} + +func (r Repository[T]) FirstOrCreate(model *T, conditions ...interface{}) error { + if re := r.Db.FirstOrCreate(model, conditions); re.Error != nil { + return re.Error + } + return nil +}