-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
183 lines (153 loc) · 5.67 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"sync"
"time"
"weather-monitoring/internal/alerts"
"weather-monitoring/internal/api"
"weather-monitoring/internal/storage"
"gopkg.in/yaml.v2"
"github.com/gorilla/handlers" // Importing gorilla/handlers for CORS
"github.com/joho/godotenv" // Importing godotenv to load .env file
)
// Config structure for application configuration
type Config struct {
APIKey string `yaml:"openweather_api_key"`
PollInterval int `yaml:"poll_interval"`
DBPath string `yaml:"db_path"`
AlertTempThreshold float64 `yaml:"alert_temp_threshold"`
AlertConsecutive int `yaml:"alert_consecutive_updates"`
Cities []string `yaml:"cities"`
}
// WeatherData structure for storing weather information
type WeatherData struct {
City string `json:"city"`
Temperature float64 `json:"temperature"`
Condition string `json:"condition"`
Humidity int `json:"humidity"`
WindSpeed float64 `json:"wind_speed"`
}
var (
weatherCache = make(map[string]WeatherData)
mu sync.Mutex // Protect access to weatherCache
config Config // Global config variable
)
// Load configuration from YAML file
func loadConfig() (Config, error) {
var config Config
// Load environment variables from .env file
err := godotenv.Load()
if err != nil {
log.Println("Error loading .env file")
}
data, err := ioutil.ReadFile("config/config.yaml")
if err != nil {
return config, err
}
err = yaml.Unmarshal(data, &config)
if err != nil {
log.Println("Failed to parse config.yaml:", err)
}
// Set API Key from environment variable
config.APIKey = os.Getenv("OPENWEATHER_API_KEY")
if config.APIKey == "" {
return config, fmt.Errorf("OPENWEATHER_API_KEY is not set in the environment")
}
return config, err
}
// Monitor weather for a specific city
func monitorWeather(city string) {
// Fetch initial weather data
weatherData, err := api.FetchWeatherData(config.APIKey, city)
if err != nil {
log.Printf("Error fetching weather data for %s: %v", city, err)
return
}
// Update the cache with initial weather data
mu.Lock()
weatherCache[city] = WeatherData{
City: city,
Temperature: weatherData.Main.Temp,
Condition: weatherData.Weather[0].Main,
Humidity: weatherData.Main.Humidity,
WindSpeed: weatherData.Wind.Speed,
}
mu.Unlock()
// Check for alerts
alerts.CheckAndAlert(city, weatherData.Main.Temp, config.AlertTempThreshold, config.AlertConsecutive)
// Set up periodic fetching of weather data
ticker := time.NewTicker(time.Duration(config.PollInterval) * time.Minute)
for range ticker.C {
weatherData, err := api.FetchWeatherData(config.APIKey, city)
if err != nil {
log.Printf("Error fetching weather data for %s: %v", city, err)
continue
}
// Update the cache with latest weather data
mu.Lock()
weatherCache[city] = WeatherData{
City: city,
Temperature: weatherData.Main.Temp,
Condition: weatherData.Weather[0].Main,
Humidity: weatherData.Main.Humidity,
WindSpeed: weatherData.Wind.Speed,
}
mu.Unlock()
// Check for alerts
alerts.CheckAndAlert(city, weatherData.Main.Temp, config.AlertTempThreshold, config.AlertConsecutive)
}
}
// HTTP handler to serve weather and forecast data
func weatherHandler(w http.ResponseWriter, r *http.Request) {
mu.Lock()
defer mu.Unlock()
w.Header().Set("Content-Type", "application/json")
combinedWeatherData := make(map[string]interface{})
for city, weather := range weatherCache {
// Fetch forecast data for the city
forecastData, err := api.FetchForecastData(config.APIKey, city) // Use global config here
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// Prepare combined data
combinedWeatherData[city] = map[string]interface{}{
"current_weather": weather,
"forecast": forecastData,
}
}
if err := json.NewEncoder(w).Encode(combinedWeatherData); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
// Main function to start the application
func main() {
var err error
config, err = loadConfig() // Load config and assign to global variable
if err != nil {
log.Fatalf("Error loading config: %v", err)
}
db, err := storage.InitDB(config.DBPath)
if err != nil {
log.Fatalf("Error initializing database: %v", err)
}
defer db.Close()
log.Printf("Monitoring weather for cities: %v", config.Cities)
// Start monitoring weather for each city in a separate goroutine
for _, city := range config.Cities {
go monitorWeather(city) // No need to pass config here
}
// Start the HTTP server with CORS enabled
http.HandleFunc("/weather", weatherHandler)
log.Println("Starting HTTP server on :8080")
// Setting up CORS for the server
allowedOrigins := handlers.AllowedOrigins([]string{"*"}) // Change to your allowed origins
allowedMethods := handlers.AllowedMethods([]string{"GET", "POST", "OPTIONS"})
allowedHeaders := handlers.AllowedHeaders([]string{"Content-Type", "Authorization"})
log.Fatal(http.ListenAndServe(":8080", handlers.CORS(allowedOrigins, allowedMethods, allowedHeaders)(http.DefaultServeMux)))
}