package main import ( "database/sql" "fmt" "go-weather/config" "go-weather/handlers" "go-weather/middleware" "go-weather/services" "log" "log/slog" "net/http" "os" "os/signal" "time" _ "github.com/lib/pq" ) func startPoller(db *sql.DB, ws *services.WeatherService, defaultCity string, interval time.Duration) { // Use the service weather, err := ws.FetchWeather(defaultCity) if err != nil { log.Printf("poller: fetch failed: %v", err) } else { ds := services.NewDatabaseService(db) id, err := ds.SaveReading(weather) if err != nil { log.Printf("poller: save failed %v", err) } else { log.Printf("poller: saved reading #%d for %s (%.1f F)", id, weather.Name, weather.Main.Temp) } } ticker := time.NewTicker(interval) defer ticker.Stop() for range ticker.C { weather, err := ws.FetchWeather(defaultCity) if err != nil { log.Printf("poller fetch failed: %v", err) continue } ds := services.NewDatabaseService(db) id, err := ds.SaveReading(weather) if err != nil { log.Printf("poller: save failed: %v", err) continue } log.Printf("poller: saved reading #%d for %s (%.1f F)", id, weather.Name, weather.Main.Temp) } } func main() { config := config.Load() // Setup logger var handler slog.Handler handler = slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug}) slog.SetDefault(slog.New(handler)) // Connect to DB connStr := fmt.Sprintf( "host=%s port=%s user=%s password=%s dbname=%s sslmode=disable", config.DBHost, config.DBPort, config.DBUser, config.DBPassword, config.DBName, ) database, err := services.ConnectDB(connStr) if err != nil { log.Fatalf("failed to connect to db: %v", err) } defer database.Close() // Create services databaseService := services.NewDatabaseService(database) weatherService := services.NewWeatherService(config.APIKey) userService := services.NewUserService(database) authService := services.NewAuthService(config.JWTSecret) // Create route handlers weatherHandler := handlers.NewWeatherHandler(weatherService, databaseService, config.DefaultCity) userHandler := handlers.NewUserHandler(userService, authService) // Start background poller go startPoller(database, weatherService, config.DefaultCity, 10*time.Minute) // Register routes http.HandleFunc("/login", userHandler.Login) http.HandleFunc("/logout", userHandler.Logout) http.Handle("/weather/fetch", middleware.AuthMiddleware(authService)( http.HandlerFunc(weatherHandler.FetchWeather)), ) http.Handle("/me", http.HandlerFunc(userHandler.Me)) http.HandleFunc("/weather/history", weatherHandler.GetHistory) http.HandleFunc("/health", handlers.Health) http.HandleFunc("/latest-temperature", handlers.LatestTemperature) http.HandleFunc("/test", handlers.Test) http.HandleFunc("/register", userHandler.Register) // Start server go func() { fmt.Println("server listening on :8080") if err := http.ListenAndServe(":8080", nil); err != nil { log.Fatalf("server failed: %v", err) } }() // Wait for shutdown quit := make(chan os.Signal, 1) signal.Notify(quit, os.Interrupt) <-quit fmt.Println("\nshutting down...") }