From c3c7e908f008d3869a97fdf0540a4c33d9b8f463 Mon Sep 17 00:00:00 2001 From: AmirMahdi Qiasvand Date: Sat, 6 Sep 2025 12:58:42 +0330 Subject: [PATCH] feat: setup config (viper =)), jwt settings && session struct --- config/.gitkeep | 0 config/config.go | 33 +++++++++++++++++++++ config/read.go | 51 ++++++++++++++++++++++++++++++++ go.mod | 17 ++++++++++- go.sum | 48 +++++++++++++++++++++++++++--- internal/api/.gitkeep | 0 internal/app/app.go | 1 + internal/domain/user/types.go | 10 +++++++ internal/usecase/.gitkeep | 0 internal/usecase/auth_service.go | 1 + pkg/jwt/claims.go | 9 ++++++ pkg/jwt/jwt.go | 37 +++++++++++++++++++++++ 12 files changed, 202 insertions(+), 5 deletions(-) delete mode 100644 config/.gitkeep create mode 100644 config/config.go create mode 100644 config/read.go delete mode 100644 internal/api/.gitkeep create mode 100644 internal/app/app.go delete mode 100644 internal/usecase/.gitkeep create mode 100644 internal/usecase/auth_service.go create mode 100644 pkg/jwt/claims.go create mode 100644 pkg/jwt/jwt.go diff --git a/config/.gitkeep b/config/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..fd9410a --- /dev/null +++ b/config/config.go @@ -0,0 +1,33 @@ +package config + +type Config struct { + Server Server `mapstructure:"server"` + JWT JWT `mapstructure:"jwt"` + DB DB `mapstructure:"db"` + Redis Redis `mapstructure:"redis"` +} + +type Server struct { + Host string `mapstructure:"host"` + Port int `mapstructure:"port"` +} + +type DB struct { + User string `mapstructure:"user"` + Pass string `mapstructure:"pass"` + Host string `mapstructure:"host"` + Port int `mapstructure:"port"` + DBName string `mapstructure:"db_name"` +} + +type Redis struct { + Host string `mapstructure:"host"` + Port int `mapstructure:"port"` + Pass string `mapstructure:"pass"` +} + +type JWT struct { + TokenExpMinutes uint `mapstructure:"token_exp_minutes"` + RefreshTokenExpMinutes uint `mapstructure:"refresh_token_exp_minutes"` + TokenSecret string `mapstructure:"token_secret"` +} diff --git a/config/read.go b/config/read.go new file mode 100644 index 0000000..ec22dc3 --- /dev/null +++ b/config/read.go @@ -0,0 +1,51 @@ +package config + +import ( + "path/filepath" + + "github.com/spf13/viper" +) + +func ReadGeneric[T any](cfgPath string) (T, error) { + var cfg T + fullAbsPath, err := absPath(cfgPath) + if err != nil { + return cfg, err + } + + // configPath := filepath.Dir(fullAbsPath) + // viper.AddConfigPath(configPath) + // configType := strings.TrimPrefix(filepath.Ext(fullAbsPath), ".") + // viper.SetConfigType(configType) + // configFile := strings.TrimSuffix(filepath.Base(fullAbsPath), filepath.Ext(fullAbsPath)) + // viper.SetConfigName(configFile) + + viper.SetConfigFile(fullAbsPath) + + viper.AutomaticEnv() + + if err := viper.ReadInConfig(); err != nil { + return cfg, err + } + + return cfg, viper.Unmarshal(&cfg) +} + +func ReadStandard(cfgPath string) (Config, error) { + return ReadGeneric[Config](cfgPath) +} + +func absPath(cfgPath string) (string, error) { + if !filepath.IsAbs(cfgPath) { + return filepath.Abs(cfgPath) + } + return cfgPath, nil +} + +func MustReadStandard(configPath string) Config { + cfg, err := ReadStandard(configPath) + if err != nil { + panic(err) + } + return cfg +} diff --git a/go.mod b/go.mod index 7ecdcdf..06ee044 100644 --- a/go.mod +++ b/go.mod @@ -3,19 +3,34 @@ module backend go 1.25.0 require ( + github.com/golang-jwt/jwt/v5 v5.3.0 github.com/google/uuid v1.6.0 + github.com/spf13/viper v1.20.1 gorm.io/driver/postgres v1.6.0 gorm.io/gorm v1.30.2 ) require ( + github.com/fsnotify/fsnotify v1.8.0 // indirect + github.com/go-viper/mapstructure/v2 v2.2.1 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/pgx/v5 v5.6.0 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect - golang.org/x/crypto v0.31.0 // indirect + github.com/pelletier/go-toml/v2 v2.2.3 // indirect + github.com/sagikazarmark/locafero v0.7.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.12.0 // indirect + github.com/spf13/cast v1.7.1 // indirect + github.com/spf13/pflag v1.0.6 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + go.uber.org/atomic v1.9.0 // indirect + go.uber.org/multierr v1.9.0 // indirect + golang.org/x/crypto v0.32.0 // indirect golang.org/x/sync v0.10.0 // indirect + golang.org/x/sys v0.29.0 // indirect golang.org/x/text v0.21.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index dca6894..d92b739 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,16 @@ 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= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= +github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= +github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= +github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +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/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= @@ -15,20 +25,50 @@ 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/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= +github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= 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/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= +github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= +github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4= +github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= +github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= +github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= -golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= +go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= +golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= +golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.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= diff --git a/internal/api/.gitkeep b/internal/api/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/internal/app/app.go b/internal/app/app.go new file mode 100644 index 0000000..4879f7a --- /dev/null +++ b/internal/app/app.go @@ -0,0 +1 @@ +package app diff --git a/internal/domain/user/types.go b/internal/domain/user/types.go index fc437ac..724d838 100644 --- a/internal/domain/user/types.go +++ b/internal/domain/user/types.go @@ -16,3 +16,13 @@ type User struct { UpdatedAt time.Time DeletedAt *time.Time } + +type UserSession struct { + ID uuid.UUID + UserID uuid.UUID + WalletID uuid.UUID + IPaddress string + Agent string + ExpiresAt time.Time + CreatedAt time.Time +} diff --git a/internal/usecase/.gitkeep b/internal/usecase/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/internal/usecase/auth_service.go b/internal/usecase/auth_service.go new file mode 100644 index 0000000..aed2454 --- /dev/null +++ b/internal/usecase/auth_service.go @@ -0,0 +1 @@ +package usecase diff --git a/pkg/jwt/claims.go b/pkg/jwt/claims.go new file mode 100644 index 0000000..1088e05 --- /dev/null +++ b/pkg/jwt/claims.go @@ -0,0 +1,9 @@ +package jwt + +import jwt2 "github.com/golang-jwt/jwt/v5" + +type UserClaims struct { + jwt2.RegisteredClaims + UserID uint + Sections []string +} diff --git a/pkg/jwt/jwt.go b/pkg/jwt/jwt.go new file mode 100644 index 0000000..6f037c3 --- /dev/null +++ b/pkg/jwt/jwt.go @@ -0,0 +1,37 @@ +package jwt + +import ( + "errors" + + jwt2 "github.com/golang-jwt/jwt/v5" +) + +const UserClaimKey = "User-Claims" + +func CreateToken(secret []byte, claims *UserClaims) (string, error) { + return jwt2.NewWithClaims(jwt2.SigningMethodHS512, claims).SignedString(secret) +} + +func ParseToken(tokenString string, secret []byte) (*UserClaims, error) { + token, err := jwt2.ParseWithClaims(tokenString, &UserClaims{}, func(t *jwt2.Token) (interface{}, error) { + return secret, nil + }) + + var claim *UserClaims + if token.Claims != nil { + cc, ok := token.Claims.(*UserClaims) + if ok { + claim = cc + } + } + + if err != nil { + return claim, err + } + + if !token.Valid { + return claim, errors.New("token is not valid") + } + + return claim, nil +}