85 lines
2.9 KiB
JavaScript
85 lines
2.9 KiB
JavaScript
const express = require("express");
|
|
const axios = require("axios");
|
|
const config = require("../config/api");
|
|
|
|
const router = express.Router();
|
|
|
|
router.get("/", async (req, res) => {
|
|
try {
|
|
const { apiKey, baseUrl, city, units, language } = config.weather;
|
|
if (!apiKey) {
|
|
return res.status(400).json({ message: "OPENWEATHER_API_KEY is not set" });
|
|
}
|
|
|
|
const { q, lat, lon } = req.query;
|
|
const params = {
|
|
appid: apiKey,
|
|
units,
|
|
lang: language,
|
|
};
|
|
|
|
let currentResponse, forecastResponse, airResponse;
|
|
|
|
if (lat && lon) {
|
|
// 1. Parallel fetch for all 3 endpoints if coordinates are available
|
|
[currentResponse, forecastResponse, airResponse] = await Promise.all([
|
|
axios.get(`${baseUrl}/weather`, { params: { ...params, lat, lon } }),
|
|
axios.get(`${baseUrl}/forecast`, { params: { ...params, lat, lon } }),
|
|
axios.get(`${baseUrl}/air_pollution`, { params: { lat, lon, appid: apiKey } }),
|
|
]);
|
|
} else {
|
|
// 2. Sequential fallback if only city name is provided
|
|
params.q = q || city;
|
|
currentResponse = await axios.get(`${baseUrl}/weather`, { params });
|
|
const { coord } = currentResponse.data;
|
|
|
|
[forecastResponse, airResponse] = await Promise.all([
|
|
axios.get(`${baseUrl}/forecast`, {
|
|
params: { ...params, lat: coord.lat, lon: coord.lon },
|
|
}),
|
|
axios.get(`${baseUrl}/air_pollution`, {
|
|
params: { lat: coord.lat, lon: coord.lon, appid: apiKey },
|
|
}),
|
|
]);
|
|
}
|
|
|
|
const currentData = currentResponse.data;
|
|
|
|
// 3. Process Forecast for Today's Min/Max (Rolling 24-hour window for stability)
|
|
// We use the first 8 segments (3h * 8 = 24h) to ensure we always have a full
|
|
// day-night cycle of temperatures, providing a stable daily range.
|
|
const forecastList = forecastResponse.data.list;
|
|
if (forecastList && forecastList.length > 0) {
|
|
const next24h = forecastList.slice(0, 8);
|
|
|
|
// Calculate min/max from forecast
|
|
let minTemp = Math.min(...next24h.map((item) => item.main.temp_min));
|
|
let maxTemp = Math.max(...next24h.map((item) => item.main.temp_max));
|
|
|
|
// Also compare with current temperature to ensure the range covers current state
|
|
const currentTemp = currentData.main.temp;
|
|
minTemp = Math.min(minTemp, currentTemp);
|
|
maxTemp = Math.max(maxTemp, currentTemp);
|
|
|
|
currentData.main.temp_min = minTemp;
|
|
currentData.main.temp_max = maxTemp;
|
|
}
|
|
|
|
// 4. Attach Air Quality (AQI)
|
|
// OpenWeatherMap AQI: 1 (Good) ... 5 (Very Poor)
|
|
if (airResponse.data.list && airResponse.data.list.length > 0) {
|
|
currentData.aqi = airResponse.data.list[0].main.aqi;
|
|
}
|
|
|
|
res.json(currentData);
|
|
} catch (error) {
|
|
console.error("Weather API Error:", error.message);
|
|
if (error.response) {
|
|
console.error("Data:", error.response.data);
|
|
}
|
|
res.status(500).json({ message: "Failed to fetch weather" });
|
|
}
|
|
});
|
|
|
|
module.exports = router;
|