package main import ( "os" "os/signal" "syscall" "git.intra.yksa.space/gsn/predictor/internal/jobs/grib/updater" "git.intra.yksa.space/gsn/predictor/internal/pkg/errcodes" "git.intra.yksa.space/gsn/predictor/internal/pkg/grib" "git.intra.yksa.space/gsn/predictor/internal/service" "git.intra.yksa.space/gsn/predictor/internal/transport/rest" "git.intra.yksa.space/gsn/predictor/internal/transport/rest/handler" "git.intra.yksa.space/gsn/predictor/pkg/redis" "git.intra.yksa.space/gsn/predictor/pkg/scheduler" env "github.com/caarlos0/env/v11" "go.uber.org/zap" ) const servicePrefix = "GSN_PREDICTOR" func main() { lg, err := zap.NewProduction() if err != nil { panic(err) } defer lg.Sync() // Load configuration from environment with service prefix cfg, err := loadConfig() if err != nil { lg.Fatal("failed to load configuration", zap.Error(err)) } // Load scheduler configuration schedulerConfig, err := loadSchedulerConfig() if err != nil { lg.Fatal("failed to load scheduler configuration", zap.Error(err)) } // Load GRIB updater job configuration gribUpdaterConfig, err := loadGribUpdaterConfig() if err != nil { lg.Fatal("failed to load GRIB updater configuration", zap.Error(err)) } // Initialize Redis service redisService, err := redis.New(cfg.Redis) if err != nil { lg.Fatal("failed to initialize Redis service", zap.Error(err)) } defer redisService.Close() // Initialize GRIB service gribService, err := grib.New(grib.ServiceConfig{ Dir: cfg.Grib.Dir, TTL: cfg.Grib.TTL, CacheTTL: cfg.Grib.CacheTTL, Redis: redisService, Parallel: cfg.Grib.Parallel, Client: cfg.CreateHTTPClient(), }) if err != nil { lg.Fatal("failed to initialize GRIB service", zap.Error(err)) } defer gribService.Close() // Initialize service with dependencies svc, err := service.New(cfg, gribService, redisService, lg) if err != nil { lg.Fatal("failed to initialize service", zap.Error(err)) } defer svc.Close() // Initialize scheduler var sched *scheduler.Scheduler if schedulerConfig.Enabled { sched = scheduler.New(lg) // Add GRIB update job gribJob := updater.New(gribService, gribUpdaterConfig, lg) if err := sched.AddJob(gribJob); err != nil { lg.Error("failed to add GRIB update job to scheduler", zap.Error(err)) } // TODO: Add more jobs here as needed // Example: // cleanupConfig := cleanup.NewConfig() // cleanupJob := cleanup.New(svc, cleanupConfig, lg) // if err := sched.AddJob(cleanupJob); err != nil { // lg.Error("failed to add cleanup job to scheduler", zap.Error(err)) // } lg.Info("scheduler initialized with jobs") } // Initialize handler handler := handler.New(svc) // Initialize transport restConfig, err := loadRestConfig() if err != nil { lg.Fatal("failed to init transport config", zap.Error(err)) } transport, err := rest.New(lg, handler, restConfig) if err != nil { lg.Fatal("failed to init transport", zap.Error(err)) } // Start service svc.Start() // Start scheduler if enabled if sched != nil { sched.Start() lg.Info("scheduler started") } lg.Info("service started successfully", zap.String("grib_dir", cfg.Grib.Dir), zap.Duration("grib_ttl", cfg.Grib.TTL), zap.Duration("grib_cache_ttl", cfg.Grib.CacheTTL), zap.Int("grib_parallel", cfg.Grib.Parallel), zap.Bool("scheduler_enabled", schedulerConfig.Enabled), zap.Duration("grib_update_interval", gribUpdaterConfig.Interval)) // Wait for shutdown signal sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) // Start server in goroutine go func() { lg.Info("starting HTTP server") transport.Run() }() // Wait for shutdown signal <-sigChan lg.Info("received shutdown signal, stopping service") // Stop scheduler first if sched != nil { sched.Stop() lg.Info("scheduler stopped") } } // loadConfig loads configuration from environment with service prefix func loadConfig() (*service.Config, error) { cfg := &service.Config{} if err := env.ParseWithOptions(cfg, env.Options{ PrefixTagName: servicePrefix + "_", }); err != nil { return nil, errcodes.Wrap(err, "failed to parse configuration") } return cfg, nil } // loadSchedulerConfig loads scheduler configuration from environment func loadSchedulerConfig() (*scheduler.Config, error) { cfg := &scheduler.Config{} if err := env.ParseWithOptions(cfg, env.Options{ PrefixTagName: servicePrefix + "_SCHEDULER_", }); err != nil { return nil, errcodes.Wrap(err, "failed to parse scheduler configuration") } return cfg, nil } // loadGribUpdaterConfig loads GRIB updater job configuration from environment func loadGribUpdaterConfig() (*updater.Config, error) { cfg := &updater.Config{} if err := env.ParseWithOptions(cfg, env.Options{ PrefixTagName: servicePrefix + "_GRIB_UPDATER_", }); err != nil { return nil, errcodes.Wrap(err, "failed to parse GRIB updater configuration") } return cfg, nil } // loadRestConfig loads REST transport configuration from environment with service prefix func loadRestConfig() (*rest.Config, error) { cfg := &rest.Config{} if err := env.ParseWithOptions(cfg, env.Options{ PrefixTagName: servicePrefix + "_REST_", }); err != nil { return nil, errcodes.Wrap(err, "failed to parse REST configuration") } return cfg, nil }