package main import ( "context" "fmt" "net" "os" "os/signal" "syscall" "time" "lendry-erp/media/internal/application/usecases" "lendry-erp/media/internal/config" infrastructureGrpc "lendry-erp/media/internal/infrastructure/grpc" "lendry-erp/media/internal/infrastructure/images" "lendry-erp/media/internal/infrastructure/storage" "lendry-erp/media/pkg/logger" ) func main() { cfg := config.Load() logger.Init(cfg.Logging.Level) logger.Info("πŸš€ Starting media-service (gRPC only) in %s mode", cfg.App.Env) // 1. Π˜Π½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΡ Ρ…Ρ€Π°Π½ΠΈΠ»ΠΈΡ‰Π° mediaStorage, err := storage.NewS3Storage(cfg) if err != nil { logger.Fatal("failed to init S3 storage: %v", err) } logger.Info("βœ… S3 storage connected (bucket: %s)", cfg.Storage.Bucket) // 2. Π˜Π½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΡ UseCases (БизнСс-Π»ΠΎΠ³ΠΈΠΊΠ°) imgProcessor := images.NewImageProcessor() uploadUC := usecases.NewUploadUseCase(mediaStorage, imgProcessor) presignUC := usecases.NewPresignUseCase(mediaStorage) // 3. Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ gRPC сСрвСра grpcServer := infrastructureGrpc.NewServer(uploadUC, presignUC) // 4. Запуск ΠΏΡ€ΠΎΡΠ»ΡƒΡˆΠΈΠ²Π°Π½ΠΈΡ ΠΏΠΎΡ€Ρ‚Π° grpcListener, err := net.Listen("tcp", fmt.Sprintf(":%s", cfg.GRPC.Port)) if err != nil { logger.Fatal("failed to listen gRPC: %v", err) } go func() { logger.Info("gRPC listening on :%s", cfg.GRPC.Port) if err := grpcServer.Serve(grpcListener); err != nil { logger.Fatal("gRPC serve error: %v", err) } }() // 5. ОТиданиС сигнала остановки waitForShutdown(func() { logger.Warn("πŸ›‘ Graceful shutdown started...") grpcServer.GracefulStop() mediaStorage.Close() logger.Info("βœ… Shutdown complete") }) } func waitForShutdown(cleanup func()) { quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() cleanup() <-ctx.Done() }