package main import ( "fmt" "log/slog" "os" "os/signal" "syscall" "time" "github.com/sentinela-go/internal/api" "github.com/sentinela-go/internal/config" "github.com/sentinela-go/internal/db" "github.com/sentinela-go/internal/fetcher" ) func main() { cfg := config.Load() // Setup structured logging level := slog.LevelInfo if cfg.LogLevel == "debug" { level = slog.LevelDebug } slog.SetDefault(slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: level}))) slog.Info("starting Sentinela", "port", cfg.Port) // Initialize database database, err := db.New(cfg.DatabasePath) if err != nil { slog.Error("failed to initialize database", "error", err) os.Exit(1) } defer database.Close() // Seed data if empty if database.IsMarketEmpty() { slog.Info("database is empty, seeding BCB data...") if err := fetcher.FetchAllBCB(database); err != nil { slog.Error("failed to seed BCB data", "error", err) } } if database.IsEmpty() { slog.Info("no companies found, seeding CVM data...") if err := fetcher.FetchAllCVM(database); err != nil { slog.Error("failed to seed CVM data", "error", err) } } // Seed test API key if _, err := database.CreateAPIKeyWithValue("sentinela-test-key-2026", "Test User", "test@sentinela.dev", "platinum"); err != nil { slog.Warn("failed to seed test API key", "error", err) } // Start scheduler syncInterval, err := time.ParseDuration(cfg.SyncInterval) if err != nil { syncInterval = 30 * time.Minute } stopChan := make(chan struct{}) go fetcher.StartScheduler(database, syncInterval, stopChan) // Create and start server app := api.NewServer(cfg, database) // Graceful shutdown go func() { sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) <-sigChan slog.Info("shutting down...") close(stopChan) app.Shutdown() }() addr := fmt.Sprintf(":%d", cfg.Port) if err := app.Listen(addr); err != nil { slog.Error("server error", "error", err) os.Exit(1) } }