diff --git a/internal/app/app.go b/internal/app/app.go index 77fafcf..0abef64 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -9,6 +9,8 @@ import ( "codeword/internal/services" "codeword/internal/utils/encrypt" "context" + "fmt" + "go.mongodb.org/mongo-driver/mongo" "go.uber.org/zap" "time" ) @@ -29,8 +31,16 @@ func Run(ctx context.Context, cfg initialize.Config, logger *zap.Logger) error { }) userRepo := repository.NewUserRepository(mdb) + recoveryEmailSender := &client.RecoveryEmailSender{} - recoveryService := services.NewRecoveryService(logger, userRepo, recoveryEmailSender, encryptService) + + recoveryService := services.NewRecoveryService(services.Deps{ + Logger: logger, + Repository: userRepo, + Email: recoveryEmailSender, + EncryptService: encryptService, + }) + recoveryController := controller.NewRecoveryController(logger, recoveryService) server := httpserver.NewServer(httpserver.ServerConfig{ @@ -47,19 +57,46 @@ func Run(ctx context.Context, cfg initialize.Config, logger *zap.Logger) error { <-ctx.Done() - if err := shutdownApp(server, logger); err != nil { + fmt.Println("<-ctx.Done()") + + if err := shutdownApp(server, mdb, logger); err != nil { return err } logger.Info("Приложение остановлено") return nil } -func shutdownApp(server *httpserver.Server, logger *zap.Logger) error { +// TODO возможно стоит вынести в отдельные файлы или отказаться от разделения на отдельные методы + +func shutdownApp(server *httpserver.Server, mdb *mongo.Database, logger *zap.Logger) error { + if err := shutdownHTTPServer(server, logger); err != nil { + return err + } + + if err := shutdownMongoDB(mdb, logger); err != nil { + return err + } + + return nil +} + +func shutdownHTTPServer(server *httpserver.Server, logger *zap.Logger) error { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := server.Shutdown(ctx); err != nil { - logger.Error("Ошибка при остановке сервера Fiber", zap.Error(err)) + logger.Error("Ошибка при остановке HTTP-сервера", zap.Error(err)) + return err + } + return nil +} + +func shutdownMongoDB(mdb *mongo.Database, logger *zap.Logger) error { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + if err := mdb.Client().Disconnect(ctx); err != nil { + logger.Error("Ошибка при закрытии соединения с MongoDB", zap.Error(err)) return err } return nil diff --git a/internal/controller/recovery/recovery_controller.go b/internal/controller/recovery/recovery_controller.go index 6a0420c..d2a5330 100644 --- a/internal/controller/recovery/recovery_controller.go +++ b/internal/controller/recovery/recovery_controller.go @@ -9,14 +9,14 @@ import ( ) type RecoveryController struct { - Logger *zap.Logger - Service *services.RecoveryService + logger *zap.Logger + service *services.RecoveryService } func NewRecoveryController(logger *zap.Logger, service *services.RecoveryService) *RecoveryController { return &RecoveryController{ - Logger: logger, - Service: service, + logger: logger, + service: service, } } @@ -24,31 +24,31 @@ func NewRecoveryController(logger *zap.Logger, service *services.RecoveryService func (r *RecoveryController) HandleRecoveryRequest(c *fiber.Ctx) error { email := c.FormValue("email") - key, err := r.Service.GenerateKey() + key, err := r.service.GenerateKey() if err != nil { - r.Logger.Error("Failed to generate key", zap.Error(err)) + r.logger.Error("Failed to generate key", zap.Error(err)) return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"}) } fmt.Println(key) - user, err := r.Service.FindUserByEmail(email) + user, err := r.service.FindUserByEmail(email) if err != nil { - r.Logger.Error("Failed to find user by email", zap.Error(err)) + r.logger.Error("Failed to find user by email", zap.Error(err)) return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "User not found"}) } fmt.Println(user) // сохраняем в бд - signature, err := r.Service.StoreRecoveryRecord("user") + signature, err := r.service.StoreRecoveryRecord("user") if err != nil { - r.Logger.Error("Failed to store recovery record", zap.Error(err)) + r.logger.Error("Failed to store recovery record", zap.Error(err)) return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"}) } // тут что-то на подобии канала или что-то подобное так как отправка выполнятеся в воркере, //это пока временное решение для написания структуры кода и проверки отправки, далее перепишу // под горутины - err = r.Service.SendRecoveryEmail(email, signature) + err = r.service.SendRecoveryEmail(email, signature) if err != nil { - r.Logger.Error("Failed to send recovery email", zap.Error(err)) + r.logger.Error("Failed to send recovery email", zap.Error(err)) return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"}) } @@ -59,21 +59,21 @@ func (r *RecoveryController) HandleRecoveryRequest(c *fiber.Ctx) error { func (r *RecoveryController) HandleRecoveryLink(c *fiber.Ctx) error { signature := c.Params("sign") // тут получается - record, err := r.Service.GetRecoveryRecord(signature) + record, err := r.service.GetRecoveryRecord(signature) if err != nil { - r.Logger.Error("Failed to get recovery record", zap.Error(err)) + r.logger.Error("Failed to get recovery record", zap.Error(err)) return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"}) } // проверка на более чем 15 минут if time.Since(record.CreatedAt) > 15*time.Minute { - r.Logger.Error("Recovery link expired", zap.String("signature", signature)) + r.logger.Error("Recovery link expired", zap.String("signature", signature)) return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Recovery link expired"}) } - tokens, err := r.Service.ExchangeForTokens(record.UserID) + tokens, err := r.service.ExchangeForTokens(record.UserID) if err != nil { - r.Logger.Error("Failed to exchange recovery link for tokens", zap.Error(err)) + r.logger.Error("Failed to exchange recovery link for tokens", zap.Error(err)) return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"}) } diff --git a/internal/initialize/config.go b/internal/initialize/config.go index 79d5870..540a454 100644 --- a/internal/initialize/config.go +++ b/internal/initialize/config.go @@ -14,9 +14,9 @@ type Config struct { MongoPassword string `env:"MONGO_PASSWORD" envDefault:"admin"` MongoDatabase string `env:"MONGO_DB" envDefault:"codeword_db"` MongoAuth string `env:"MONGO_AUTH" envDefault:"admin"` - PublicCurveKey string `env:"PUBLIC_CURVE_KEY,required"` - PrivateCurveKey string `env:"PRIVATE_CURVE_KEY,required"` - SignSecret string `env:"SIGN_SECRET,required"` + PublicCurveKey string `env:"PUBLIC_CURVE_KEY" envDefault:"test"` + PrivateCurveKey string `env:"PRIVATE_CURVE_KEY" envDefault:"test"` + SignSecret string `env:"SIGN_SECRET" envDefault:"test"` } func LoadConfig() (*Config, error) { diff --git a/internal/repository/user_repository.go b/internal/repository/user_repository.go index 4009049..ab9ca1b 100644 --- a/internal/repository/user_repository.go +++ b/internal/repository/user_repository.go @@ -12,6 +12,8 @@ type UserRepository struct { db *mongo.Database } +//todo реализовать + func NewUserRepository(db *mongo.Database) *UserRepository { return &UserRepository{db} } @@ -28,8 +30,6 @@ func (r *UserRepository) StoreRecoveryRecord(userID, signature string, createdAt } func (r *UserRepository) GetRecoveryRecord(signature string) (*models.RestoreRequest, error) { - //todo - return &models.RestoreRequest{UserID: "123", Sign: signature, CreatedAt: time.Now()}, nil } diff --git a/internal/server/http/http_server.go b/internal/server/http/http_server.go index 56fa3bc..1e9eb5a 100644 --- a/internal/server/http/http_server.go +++ b/internal/server/http/http_server.go @@ -38,6 +38,14 @@ func NewServer(config ServerConfig) *Server { return s } +func (s *Server) Start(addr string) error { + return s.app.Listen(addr) +} + +func (s *Server) Shutdown(ctx context.Context) error { + return s.app.Shutdown() +} + func (s *Server) registerRoutes() { s.app.Get("/liveness", s.handleLiveness) s.app.Get("/readiness", s.handleReadiness) @@ -64,11 +72,3 @@ func (s *Server) handleReadiness(c *fiber.Ctx) error { return c.Status(fiber.StatusOK).SendString(responseMessage) } - -func (s *Server) Start(addr string) error { - return s.app.Listen(addr) -} - -func (s *Server) Shutdown(ctx context.Context) error { - return s.app.Shutdown() -} diff --git a/internal/services/recovery_service.go b/internal/services/recovery_service.go index 19c1163..59ab60f 100644 --- a/internal/services/recovery_service.go +++ b/internal/services/recovery_service.go @@ -17,21 +17,26 @@ type EmailSender interface { SendRecoveryEmail(email, signature string) error } -// todo deps - -type RecoveryService struct { +type Deps struct { Logger *zap.Logger Repository UserRepository Email EmailSender EncryptService *encrypt.Encrypt } -func NewRecoveryService(logger *zap.Logger, repository UserRepository, email EmailSender, encryptService *encrypt.Encrypt) *RecoveryService { +type RecoveryService struct { + logger *zap.Logger + repository UserRepository + email EmailSender + encryptService *encrypt.Encrypt +} + +func NewRecoveryService(deps Deps) *RecoveryService { return &RecoveryService{ - Logger: logger, - Repository: repository, - Email: email, - EncryptService: encryptService, + logger: deps.Logger, + repository: deps.Repository, + email: deps.Email, + encryptService: deps.EncryptService, } } @@ -43,25 +48,25 @@ func (s *RecoveryService) GenerateKey() (string, error) { // FindUserByEmail ищет пользователя по электронной почте func (s *RecoveryService) FindUserByEmail(email string) (*models.User, error) { - return s.Repository.FindByEmail(email) + return s.repository.FindByEmail(email) } // StoreRecoveryRecord сохраняет запись восстановления в базе данных func (s *RecoveryService) StoreRecoveryRecord(userID string) (string, error) { signature := "" createdAt := time.Now() - err := s.Repository.StoreRecoveryRecord(userID, signature, createdAt) + err := s.repository.StoreRecoveryRecord(userID, signature, createdAt) return signature, err } // GetRecoveryRecord получает запись восстановления из базы данных func (s *RecoveryService) GetRecoveryRecord(signature string) (*models.RestoreRequest, error) { - return s.Repository.GetRecoveryRecord(signature) + return s.repository.GetRecoveryRecord(signature) } // SendRecoveryEmail посылает письмо для восстановления доступа пользователю func (s *RecoveryService) SendRecoveryEmail(email string, signature string) error { - return s.Email.SendRecoveryEmail(email, signature) + return s.email.SendRecoveryEmail(email, signature) } // ExchangeForTokens обменивает ссылку восстановления на токены используя сервис аутентификации.