Files
bini-google-tv/backend/routes/weather.js

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;