diff --git a/docker-compose.yaml b/docker-compose.yaml index e266cca..0b05d6c 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -14,6 +14,7 @@ services: redis: image: redis:7.4.0 container_name: redis-filekeeper + command: ["redis-server", "--requirepass", "VerySecretPassword"] networks: - filekeeper @@ -23,9 +24,9 @@ services: environment: SERVER_HOST: 0.0.0.0 SERVER_PORT: 8000 - DOMAIN: filekeeper.fossy.my.id + DOMAIN: filekeeper.example.com CORS_PROTO: https - CORS_LIST: filekeeper.fossy.my.id:443,fossy.my.id:443 + CORS_LIST: filekeeper.example.com:443,example.com:443 CORS_METHODS: POST,GET DB_HOST: postgres-filekeeper DB_PORT: 5432 @@ -48,7 +49,9 @@ services: - traefik labels: - "traefik.enable=true" - - "traefik.http.routers.filekeeper.rule=Host(`filekeeper-staging.com`)" + - "traefik.http.routers.filekeeper.rule=Host(`filekeeper.example.com`)" + - "traefik.http.routers.filekeeper.entrypoints=websecure" + - "traefik.http.routers.filekeeper.tls.certresolver=myresolver" - "traefik.http.services.filekeeper.loadbalancer.server.port=8000" depends_on: - postgres-server @@ -61,18 +64,18 @@ services: command: - "--api.insecure=true" - "--providers.docker=true" - - "--entrypoints.web.address=:80" + - "--providers.docker.exposedbydefault=false" - "--entrypoints.websecure.address=:443" - - "--certificatesresolvers.le.acme.tlschallenge=true" - - "--certificatesresolvers.le.acme.email=your-email@example.com" - - "--certificatesresolvers.le.acme.storage=/letsencrypt/acme.json" + - "--certificatesresolvers.myresolver.acme.tlschallenge=true" + - "--certificatesresolvers.myresolver.acme.email=bagas@example.com" + - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json" ports: - "80:80" - "443:443" - "8080:8080" volumes: - "/var/run/docker.sock:/var/run/docker.sock" - - "./letsencrypt:/letsencrypt" + - "/opt/letsencrypt:/letsencrypt" networks: - filekeeper - traefik diff --git a/handler/signup/signup.go b/handler/signup/signup.go index a08007d..eb13beb 100644 --- a/handler/signup/signup.go +++ b/handler/signup/signup.go @@ -3,13 +3,15 @@ package signupHandler import ( "bytes" "context" + "encoding/json" + "errors" "fmt" "github.com/fossyy/filekeeper/app" "github.com/fossyy/filekeeper/utils" "github.com/fossyy/filekeeper/view/client/email" signupView "github.com/fossyy/filekeeper/view/client/signup" + "github.com/redis/go-redis/v9" "net/http" - "sync" "time" "github.com/fossyy/filekeeper/types" @@ -20,41 +22,9 @@ import ( type UnverifiedUser struct { User *models.User Code string - mu sync.Mutex CreateTime time.Time } -var VerifyUser map[string]*UnverifiedUser -var VerifyEmail map[string]string - -func init() { - VerifyUser = make(map[string]*UnverifiedUser) - VerifyEmail = make(map[string]string) - - ticker := time.NewTicker(time.Minute) - go func() { - for { - <-ticker.C - currentTime := time.Now() - cacheClean := 0 - cleanID := utils.GenerateRandomString(10) - app.Server.Logger.Info(fmt.Sprintf("Cache cleanup [signup] [%s] initiated at %02d:%02d:%02d", cleanID, currentTime.Hour(), currentTime.Minute(), currentTime.Second())) - - for _, data := range VerifyUser { - data.mu.Lock() - if currentTime.Sub(data.CreateTime) > time.Minute*10 { - delete(VerifyUser, data.Code) - delete(VerifyEmail, data.User.Email) - cacheClean++ - } - data.mu.Unlock() - } - - app.Server.Logger.Info(fmt.Sprintf("Cache cleanup [signup] [%s] completed: %d entries removed. Finished at %s", cleanID, cacheClean, time.Since(currentTime))) - } - }() -} - func GET(w http.ResponseWriter, r *http.Request) { component := signupView.Main("Filekeeper - Sign up Page", types.Message{ Code: 3, @@ -136,16 +106,18 @@ func verifyEmail(user *models.User) error { var buffer bytes.Buffer var code string - code = VerifyEmail[user.Email] - userData, ok := VerifyUser[code] - - if !ok { - code = utils.GenerateRandomString(64) + storedCode, err := app.Server.Cache.GetCache(context.Background(), "VerificationCode:"+user.Email) + if err != nil { + if errors.Is(err, redis.Nil) { + code = utils.GenerateRandomString(64) + } else { + return err + } } else { - code = userData.Code + code = storedCode } - err := emailView.RegistrationEmail(user.Username, fmt.Sprintf("https://%s/signup/verify/%s", utils.Getenv("DOMAIN"), code)).Render(context.Background(), &buffer) + err = emailView.RegistrationEmail(user.Username, fmt.Sprintf("https://%s/signup/verify/%s", utils.Getenv("DOMAIN"), code)).Render(context.Background(), &buffer) if err != nil { return err } @@ -155,13 +127,25 @@ func verifyEmail(user *models.User) error { Code: code, CreateTime: time.Now(), } + newUnverifiedUser, err := json.Marshal(unverifiedUser) + if err != nil { + return err + } - VerifyUser[code] = &unverifiedUser - VerifyEmail[user.Email] = code + err = app.Server.Cache.SetCache(context.Background(), "UnverifiedUser:"+code, newUnverifiedUser, 10*time.Minute) + if err != nil { + return err + } + + err = app.Server.Cache.SetCache(context.Background(), "VerificationCode:"+user.Email, code, 10*time.Minute) + if err != nil { + return err + } err = app.Server.Mail.Send(user.Email, "Account Registration Verification", buffer.String()) if err != nil { return err } + return nil } diff --git a/handler/signup/verify/verify.go b/handler/signup/verify/verify.go index 5c66d0c..7eea7c7 100644 --- a/handler/signup/verify/verify.go +++ b/handler/signup/verify/verify.go @@ -1,28 +1,45 @@ package signupVerifyHandler import ( + "context" + "encoding/json" + "errors" "github.com/fossyy/filekeeper/app" + signupHandler "github.com/fossyy/filekeeper/handler/signup" signupView "github.com/fossyy/filekeeper/view/client/signup" + "github.com/redis/go-redis/v9" "net/http" - signupHandler "github.com/fossyy/filekeeper/handler/signup" "github.com/fossyy/filekeeper/types" ) func GET(w http.ResponseWriter, r *http.Request) { code := r.PathValue("code") - data, ok := signupHandler.VerifyUser[code] - if !ok { - w.WriteHeader(http.StatusNotFound) + userDataStr, err := app.Server.Cache.GetCache(context.Background(), "UnverifiedUser:"+code) + if err != nil { + if errors.Is(err, redis.Nil) { + w.WriteHeader(http.StatusNotFound) + return + } + app.Server.Logger.Error(err.Error()) + w.WriteHeader(http.StatusInternalServerError) return } - err := app.Server.Database.CreateUser(data.User) + var unverifiedUser signupHandler.UnverifiedUser + err = json.Unmarshal([]byte(userDataStr), &unverifiedUser) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + app.Server.Logger.Error(err.Error()) + return + } + + err = app.Server.Database.CreateUser(unverifiedUser.User) if err != nil { component := signupView.Main("Filekeeper - Sign up Page", types.Message{ Code: 0, - Message: "Email or Username has been registered", + Message: "Email or Username has already been registered", }) err := component.Render(r.Context(), w) if err != nil { @@ -33,11 +50,17 @@ func GET(w http.ResponseWriter, r *http.Request) { return } - delete(signupHandler.VerifyUser, code) - delete(signupHandler.VerifyEmail, data.User.Email) + err = app.Server.Cache.DeleteCache(context.Background(), "UnverifiedUser:"+code) + if err != nil { + app.Server.Logger.Error(err.Error()) + } + + err = app.Server.Cache.DeleteCache(context.Background(), "VerificationCode:"+unverifiedUser.User.Email) + if err != nil { + app.Server.Logger.Error(err.Error()) + } component := signupView.VerifySuccess("Filekeeper - Verify Page") - err = component.Render(r.Context(), w) if err != nil { w.WriteHeader(http.StatusInternalServerError) diff --git a/service/service.go b/service/service.go index cde1412..a4fd451 100644 --- a/service/service.go +++ b/service/service.go @@ -39,7 +39,7 @@ func (r *Service) GetUser(ctx context.Context, email string) (*models.User, erro } newUserJSON, _ := json.Marshal(user) - err = r.cache.SetCache(ctx, email, newUserJSON, time.Hour*24) + err = r.cache.SetCache(ctx, "UserCache:"+email, newUserJSON, time.Hour*12) if err != nil { return nil, err } diff --git a/session/session.go b/session/session.go index 0c94588..f4df181 100644 --- a/session/session.go +++ b/session/session.go @@ -54,7 +54,7 @@ func Create(values types.User) (*Session, error) { return nil, err } - err = app.Server.Cache.SetCache(context.Background(), "Session:"+id, string(sessionData), time.Hour*24) + err = app.Server.Cache.SetCache(context.Background(), "Session:"+id, string(sessionData), time.Hour*24*7) if err != nil { return nil, err } @@ -106,7 +106,7 @@ func AddSessionInfo(email string, sessionInfo *SessionInfo) error { return err } - err = app.Server.Cache.SetCache(context.Background(), "UserSessionInfo:"+email+":"+sessionInfo.SessionID, string(sessionInfoData), 0) + err = app.Server.Cache.SetCache(context.Background(), "UserSessionInfo:"+email+":"+sessionInfo.SessionID, string(sessionInfoData), time.Hour*24*7) if err != nil { return err }