diff --git a/.env b/.env index 1f5f0f141e9c1391bcd5b0325fbb516f0fa4bbfb..d4be5051a8f94bc7ee3b00530bd3191d5194a842 100644 --- a/.env +++ b/.env @@ -7,4 +7,5 @@ LOG_FLUSH_INTERVAL_MS=1000 DB_STRING="host=localhost user=ocw password=ocw dbname=ocw-db port=5433 sslmode=disable TimeZone=Asia/Shanghai" SMTP_USERNAME="noreply@ocw.id" SMTP_SERVER=localhost -SMTP_PORT=1025 \ No newline at end of file +SMTP_PORT=1025 +REDIS_STRING="localhost" \ No newline at end of file diff --git a/.env.docker b/.env.docker index 11f1edfc100a7fcebca3466da30fcdd21feff42d..2f87fea919b87db0f51f52306a1a8c21e13e3aaf 100644 --- a/.env.docker +++ b/.env.docker @@ -1,3 +1,14 @@ POSTGRES_USER=ocw POSTGRES_PASSWORD=ocw POSTGRES_DB=ocw-db +ENV=DEVELOPMENT +LISTEN_ADDR=0.0.0.0 +PORT=8080 +LOGTAIL_TOKEN= +HTTP_TIMEOUT_SEC=2 +LOG_FLUSH_INTERVAL_MS=1000 +DB_STRING="host=database user=ocw password=ocw dbname=ocw-db port=5432 sslmode=disable TimeZone=Asia/Shanghai" +SMTP_USERNAME="noreply@ocw.id" +SMTP_SERVER=mailhog +SMTP_PORT=1025 +REDIS_STRING="redis" \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 7ccabe6b9f336b3be6a3a55f4c8d11c8c2127306..3eccf485e7a93a9172dcdfca800f8bcf2b732655 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -21,7 +21,7 @@ services: - .:/app ports: - 8888:8080 - env_file: .env + env_file: .env.docker depends_on: - database - minio diff --git a/go.mod b/go.mod index b69d320688f3db0c6bbdcc1ad19eea994fde74ea..b6cf3e2e6812ad2a15b62a2272bf6fc00fc72b76 100644 --- a/go.mod +++ b/go.mod @@ -19,6 +19,11 @@ require ( gorm.io/gorm v1.24.5 ) +require ( + github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect +) + require ( github.com/DATA-DOG/go-sqlmock v1.5.0 // indirect github.com/KyleBanks/depth v1.2.1 // indirect @@ -35,6 +40,8 @@ require ( github.com/go-openapi/swag v0.22.3 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-redis/redis/v8 v8.11.5 + github.com/gomodule/redigo v1.8.9 github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect github.com/jackc/pgx/v5 v5.2.0 // indirect diff --git a/go.sum b/go.sum index d771a874f694fecc968e13f8658ba7de1cd1ca6f..104db32d430bfc3149b43928c0b68f3784d9056d 100644 --- a/go.sum +++ b/go.sum @@ -8,6 +8,8 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/caarlos0/env/v6 v6.10.1 h1:t1mPSxNpei6M5yAeu1qtRdPAK29Nbcf/n3G7x+b3/II= github.com/caarlos0/env/v6 v6.10.1/go.mod h1:hvp/ryKXKipEkcuYjs9mI4bBCg+UI0Yhgm5Zu0ddvwc= +github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cosmtrek/air v1.41.0 h1:6ck2LbcVvby6cyuwE8ruia41U2nppMZGWOpq+E/EhoU= github.com/cosmtrek/air v1.41.0/go.mod h1:+RBGjJt7T2f3I7td8Tvk0XsH/hZ3E1QBLfiWObICO4c= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -16,6 +18,8 @@ github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr 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/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= @@ -49,8 +53,12 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= 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-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= +github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/golang-jwt/jwt/v4 v4.4.3 h1:Hxl6lhQFj4AnOX6MLrsCb/+7tCj7DxP7VA+2rDIq5AU= github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws= +github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8= diff --git a/handler/reset/test.go b/handler/reset/test.go new file mode 100644 index 0000000000000000000000000000000000000000..6ca85124c85ef35e20c9dcead322ac18214844e3 --- /dev/null +++ b/handler/reset/test.go @@ -0,0 +1,21 @@ +package reset + +import ( + "fmt" + "net/http" + + "gitlab.informatika.org/ocw/ocw-backend/model/web/reset/request" +) + +func (rs ResetHandlerImpl) Test(w http.ResponseWriter, r *http.Request) { + payload := request.RequestRequestPayload{Email: "test@test.com",} + + err := rs.ResetService.Request(payload) + + if err != nil { + fmt.Print("Oh no :)") + } + + responsePayload := rs.WrapperUtil.SuccessResponseWrap(nil) + rs.HttpUtil.WriteSuccessJson(w, responsePayload) +} \ No newline at end of file diff --git a/handler/reset/types.go b/handler/reset/types.go index 7f0a8dbcfb276d41ad8067d6848d857535d739c4..91fb28636fa74b2e46ebc98b51e54a53b0747ea4 100644 --- a/handler/reset/types.go +++ b/handler/reset/types.go @@ -6,4 +6,5 @@ type ResetHandler interface { Request(w http.ResponseWriter, r *http.Request) Confirm(w http.ResponseWriter, r *http.Request) Validate(w http.ResponseWriter, r *http.Request) + Test(w http.ResponseWriter, r *http.Request) } diff --git a/model/domain/cache/cache.go b/model/domain/cache/cache.go new file mode 100644 index 0000000000000000000000000000000000000000..1cab93ca021e9cd8db82f517f00d9a2692c862c0 --- /dev/null +++ b/model/domain/cache/cache.go @@ -0,0 +1,50 @@ +package cache + +import "fmt" + +type Cache struct { + Key Key + Values []Value + ExpiryInMinutes int +} + +type Key struct { + Hash string + Id string +} + +type Value struct { + Field string + Store string +} + +func (c *Cache) AppendValue(value Value) *Cache { + c.Values = append(c.Values, value) + return c +} + +func NewKey(hash string, id string) *Key { + return &Key{hash, id} +} + +func (k Key) String() string { + return fmt.Sprintf("%s:%s", k.Hash, k.Id) +} + +func NewValue(field string, store string) *Value { + return &Value{field, store} +} + +func NewCache(key Key, initValue Value, expiryInMinutes int) *Cache { + return &Cache{key, []Value{initValue}, expiryInMinutes} +} + +func (c *Cache) Slice() ([]interface{}) { + slice := make([]interface{}, len(c.Values) * 2 + 1) + slice[0] = c.Key.String() + for i := range make([]int, len(c.Values)) { + slice[i * 2 + 1] = c.Values[i].Field + slice[i * 2 + 2] = c.Values[i].Store + } + return slice +} diff --git a/provider/di.go b/provider/di.go index 51e100b166816bee6b5d9c23147f9470831a1b9f..7b0746b2416fac00b85f4373327d116981b8a350 100644 --- a/provider/di.go +++ b/provider/di.go @@ -3,6 +3,7 @@ package provider import ( "github.com/google/wire" "gitlab.informatika.org/ocw/ocw-backend/provider/db" + "gitlab.informatika.org/ocw/ocw-backend/provider/redis" "gitlab.informatika.org/ocw/ocw-backend/provider/mail" "gitlab.informatika.org/ocw/ocw-backend/provider/mail/smtp" ) @@ -22,4 +23,8 @@ var ProviderSet = wire.NewSet( // Database utility wire.Bind(new(db.Database), new(*db.DatabaseImpl)), db.NewPostgresEnv, + + // Redis utility + wire.Bind(new(redis.Redis), new(*redis.RedisImpl)), + redis.NewRedisEnv, ) diff --git a/provider/redis/cache.go b/provider/redis/cache.go new file mode 100644 index 0000000000000000000000000000000000000000..2ffd5060c6c699f1534712a7466ec0595bcb8591 --- /dev/null +++ b/provider/redis/cache.go @@ -0,0 +1,66 @@ +package redis + +import ( + "fmt" + "os" + "runtime/debug" + "strings" + "time" + + "github.com/gomodule/redigo/redis" + "gitlab.informatika.org/ocw/ocw-backend/service/logger" + "gitlab.informatika.org/ocw/ocw-backend/utils/env" +) + +type RedisImpl struct { + pool *redis.Pool +} + +func resolver(log logger.Logger) { + if rec := recover(); rec != nil { + log.Error("Some panic occured when processing request:") + log.Error(fmt.Sprint(rec)) + log.Error("") + + log.Error("Stack Trace:") + stacks := strings.Split(string(debug.Stack()), "\n") + + for _, val := range stacks { + log.Error(val) + } + + os.Exit(-1) + } +} + +func NewRedisEnv( + env *env.Environment, + log logger.Logger, +) (*RedisImpl, error) { + return &RedisImpl{ + &redis.Pool{ + MaxIdle: 3, + IdleTimeout: 240 * time.Second, + Dial: func() (redis.Conn, error) { + defer resolver(log) + conn, err := redis.Dial("tcp", env.RedisConnection + ":" + env.RedisPort) + + if err != nil { + return nil, err + } + + return conn, err + }, + TestOnBorrow: func(c redis.Conn, t time.Time) error { + if time.Since(t) < time.Minute { + return nil + } + _, err := c.Do("PING") + return err + }, + }}, nil +} + +func (r RedisImpl) Pool() (*redis.Pool) { + return r.pool +} \ No newline at end of file diff --git a/provider/redis/type.go b/provider/redis/type.go new file mode 100644 index 0000000000000000000000000000000000000000..c2b4e2c661a7ce9851e8963e107c7fd063f04c26 --- /dev/null +++ b/provider/redis/type.go @@ -0,0 +1,7 @@ +package redis + +import "github.com/gomodule/redigo/redis" + +type Redis interface { + Pool() *redis.Pool +} \ No newline at end of file diff --git a/repository/cache/cache.go b/repository/cache/cache.go new file mode 100644 index 0000000000000000000000000000000000000000..80b61d2318848612dfbf0d2b63ebb2e260decdcd --- /dev/null +++ b/repository/cache/cache.go @@ -0,0 +1,57 @@ +package cache + +import ( + "github.com/gomodule/redigo/redis" + "gitlab.informatika.org/ocw/ocw-backend/model/domain/cache" + rd "gitlab.informatika.org/ocw/ocw-backend/provider/redis" +) + +type CacheRepositoryImpl struct { + pool *redis.Pool +} + +func New( + cache rd.Redis, +) *CacheRepositoryImpl { + return &CacheRepositoryImpl{cache.Pool()} +} + +func (c CacheRepositoryImpl) Get(cache cache.Cache, field string) (string, error) { + conn := c.pool.Get() + defer conn.Close() + + value, err := redis.String(conn.Do("HGET", cache.Key.String(), field)) + + if err != nil { + return "", err + } + + return value, nil +} + +func (c CacheRepositoryImpl) GetAll(cache cache.Cache) (map[string]string, error) { + conn := c.pool.Get() + defer conn.Close() + + value, err := redis.StringMap(conn.Do("HGETALL", cache.Key.String())) + + if err != nil { + return nil, err + } + + return value, nil +} + +func (c CacheRepositoryImpl) Set(cache cache.Cache) error { + conn := c.pool.Get() + defer conn.Close() + + slice := cache.Slice() + _, err := conn.Do("HSET", slice...) + + if err != nil { + return err + } + + return nil +} \ No newline at end of file diff --git a/repository/cache/type.go b/repository/cache/type.go new file mode 100644 index 0000000000000000000000000000000000000000..97b3691959e3bea124eb0b04a11e5f13e3fa08c6 --- /dev/null +++ b/repository/cache/type.go @@ -0,0 +1,11 @@ +package cache + +import ( + "gitlab.informatika.org/ocw/ocw-backend/model/domain/cache" +) + +type CacheRepository interface { + Get(cache cache.Cache, field string) (string, error) + GetAll(cache cache.Cache) (map[string]string, error) + Set(cache cache.Cache) error +} diff --git a/repository/di.go b/repository/di.go index ccec9594f14c22bb4d6444b2edbb26f54b8a90f3..3a1d5b9646290496eabde8604276da5c39f15332 100644 --- a/repository/di.go +++ b/repository/di.go @@ -3,12 +3,17 @@ package repository import ( "github.com/google/wire" "gitlab.informatika.org/ocw/ocw-backend/repository/user" + "gitlab.informatika.org/ocw/ocw-backend/repository/cache" ) var RepositoryBasicSet = wire.NewSet( // User Repository user.New, wire.Bind(new(user.UserRepository), new(*user.UserRepositoryImpl)), + + // Cache Repository + cache.New, + wire.Bind(new(cache.CacheRepository), new(*cache.CacheRepositoryImpl)), ) var RepositorySet = wire.NewSet( diff --git a/routes/reset/route.go b/routes/reset/route.go index 46b32e0f9f389bfe8f3ff730d2c683225dfcee91..e225524ff2141a97aa00e56f27e1e67bfd3018e2 100644 --- a/routes/reset/route.go +++ b/routes/reset/route.go @@ -14,5 +14,6 @@ func (rr ResetRoutes) Register(r chi.Router) { r.Post("/request", rr.ResetHandler.Request) r.Post("/confirm", rr.ResetHandler.Confirm) r.Post("/validate", rr.ResetHandler.Validate) + r.Get("/test", rr.ResetHandler.Test) }) } diff --git a/service/reset/impl.go b/service/reset/impl.go index b9986e2509f4af7f8c0deb9053bf8d30640e4168..ac2d214853e70fb7d61b5cab92a76f82b6584bf4 100644 --- a/service/reset/impl.go +++ b/service/reset/impl.go @@ -2,7 +2,9 @@ package reset import ( "gitlab.informatika.org/ocw/ocw-backend/repository/user" + "gitlab.informatika.org/ocw/ocw-backend/repository/cache" "gitlab.informatika.org/ocw/ocw-backend/service/verification" + "gitlab.informatika.org/ocw/ocw-backend/service/logger" "gitlab.informatika.org/ocw/ocw-backend/utils/env" "gitlab.informatika.org/ocw/ocw-backend/utils/password" "gitlab.informatika.org/ocw/ocw-backend/utils/token" @@ -10,8 +12,10 @@ import ( type ResetServiceImpl struct { user.UserRepository + cache.CacheRepository password.PasswordUtil *env.Environment token.TokenUtil verification.VerificationService + logger.Logger } diff --git a/service/reset/request.go b/service/reset/request.go index 05584e6a9bb3dcf0a7a78c4ec5e12e1c4502732d..1b90703361c00b16d4e0e2364a118dd51046c362 100644 --- a/service/reset/request.go +++ b/service/reset/request.go @@ -1,11 +1,23 @@ package reset import ( - // "gitlab.informatika.org/ocw/ocw-backend/model/domain/user" + "gitlab.informatika.org/ocw/ocw-backend/model/domain/cache" "gitlab.informatika.org/ocw/ocw-backend/model/web/reset/request" ) func (rs ResetServiceImpl) Request(payload request.RequestRequestPayload) error { - // TODO replace dummy + c := cache.NewCache(*cache.NewKey("Test", "123"), *cache.NewValue("Test", "123"), 30) + c.AppendValue(*cache.NewValue("Hello", "World")) + + err := rs.CacheRepository.Set(*c) + if err != nil { + panic(err) + } + + _, err = rs.CacheRepository.Get(*c, "Test") + if err != nil { + panic(err) + } + return nil } \ No newline at end of file diff --git a/utils/env/env.go b/utils/env/env.go index e1b914f0d6842317f23ab5288ed18f9d8a4a38b1..e688d945d6a52d2dc5a9e124111d965ed66f6b7e 100644 --- a/utils/env/env.go +++ b/utils/env/env.go @@ -40,6 +40,9 @@ type Environment struct { FrontendBaseURL string `env:"FE_BASE_URL"` ResetPasswordPath string `env:"RESET_PASSWORD_PATH" envDefault:""` EmailVerificationPath string `env:"EMAIL_VERIFICATION_PATH" envDefault:""` + + RedisConnection string `env:"REDIS_STRING"` + RedisPort string `env:"REDIS_PORT" envDefault:"6379"` } func New() (*Environment, error) {