diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d4ebdd00..a138ed34 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,9 +26,6 @@ env: UPSTASH_REDIS_REST_URL: ${{secrets.UPSTASH_REDIS_REST_URL}} UPSTASH_REDIS_REST_TOKEN: ${{secrets.UPSTASH_REDIS_REST_TOKEN}} - # Openweather - OPENWEATHER_API_KEY: ${{secrets.OPENWEATHER_API_KEY}} - jobs: main: name: CI diff --git a/src/app/(dashboard)/app/_components/weather.tsx b/src/app/(dashboard)/app/_components/weather.tsx index a0a1a280..68080ed9 100644 --- a/src/app/(dashboard)/app/_components/weather.tsx +++ b/src/app/(dashboard)/app/_components/weather.tsx @@ -40,10 +40,9 @@ export const WeatherData: FC = () => { return ( - You can expect a πŸ‘† high of {weatherData.main.temp_max.toFixed()}ΒΊ and a - πŸ‘‡ low of {weatherData.main.temp_min.toFixed()}ΒΊ with{" "} - {getFormattedWeatherDescription(weatherData.weather[0]?.description)}{" "} - today. + You can expect a high of {weatherData.temp_max.toFixed()}ΒΊ πŸ‘† and a + low of {weatherData.temp_min.toFixed()}ΒΊ πŸ‘‡ + {getFormattedWeatherDescription(weatherData.summary)} today. ); }; diff --git a/src/env.mjs b/src/env.mjs index fe0e9517..cf284977 100644 --- a/src/env.mjs +++ b/src/env.mjs @@ -12,7 +12,6 @@ export const env = createEnv({ UPSTASH_REDIS_REST_URL: z.string().optional(), UPSTASH_REDIS_REST_TOKEN: z.string().optional(), CLERK_SECRET_KEY: z.string().min(1), - OPENWEATHER_API_KEY: z.string().min(1), }, client: { NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: z.string().min(1), @@ -26,7 +25,6 @@ export const env = createEnv({ process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY, UPSTASH_REDIS_REST_TOKEN: process.env.UPSTASH_REDIS_REST_TOKEN, UPSTASH_REDIS_REST_URL: process.env.UPSTASH_REDIS_REST_URL, - OPENWEATHER_API_KEY: process.env.OPENWEATHER_API_KEY, }, skipValidation: !!process.env.SKIP_ENV_VALIDATION, }); diff --git a/src/server/api/routers/weather.ts b/src/server/api/routers/weather.ts index 4580836c..d5c5c318 100644 --- a/src/server/api/routers/weather.ts +++ b/src/server/api/routers/weather.ts @@ -1,19 +1,31 @@ import { TRPCError } from "@trpc/server"; import fetch from "node-fetch"; import { z } from "zod"; - -import { env } from "@/env.mjs"; - import { createTRPCRouter, protectedProcedure } from "../trpc"; +type RawWeatherData = { + properties: { + timeseries: { + data: { + next_12_hours: { + summary: { + symbol_code: string; + } + }, + instant: { + details: { + air_temperature: number; + } + } + } + }[] + } +} + type WeatherData = { - main: { - temp_max: number; - temp_min: number; - }; - weather: { - description: string; - }[]; + temp_max: number; + temp_min: number; + summary: string; }; export const weatherRouter = createTRPCRouter({ @@ -28,7 +40,12 @@ export const weatherRouter = createTRPCRouter({ const { latitude, longitude } = input; const response = await fetch( - `https://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&appid=${env.OPENWEATHER_API_KEY}&units=metric`, + `https://api.met.no/weatherapi/locationforecast/2.0/compact?lat=${latitude}&lon=${longitude}`, + { + headers: { + "User-Agent": `noodle.run (https://github.com/noodle-run/noodle)` + } + } ); if (!response.ok) { @@ -38,6 +55,27 @@ export const weatherRouter = createTRPCRouter({ }); } - return response.json() as Promise; + const rawWeatherData: RawWeatherData = await response.json() as RawWeatherData; + + if (rawWeatherData.properties.timeseries.length < 12) { + throw new TRPCError({ + code: "INTERNAL_SERVER_ERROR", + message: "Partial weather data" + }) + } + + const temperatures = []; + + for (const timeseries of rawWeatherData.properties.timeseries) { + temperatures.push(timeseries.data.instant.details.air_temperature); + } + + const weatherData: WeatherData = { + summary: rawWeatherData.properties.timeseries[0]!.data.next_12_hours.summary.symbol_code, + temp_max: Math.max(...temperatures), + temp_min: Math.min(...temperatures) + } + + return weatherData; }), }); diff --git a/src/utils/getFormattedWeatherDescription.ts b/src/utils/getFormattedWeatherDescription.ts index 2fa4cdc7..d159aaac 100644 --- a/src/utils/getFormattedWeatherDescription.ts +++ b/src/utils/getFormattedWeatherDescription.ts @@ -1,48 +1,50 @@ +const weatherCodeToEnglish: Record = { + "clearsky": "a clear sky", + "cloudy": "clouds", + "fair": "fair", + "fog": "fog", + "heavyrain": "heavy rain", + "heavyrainandthunder": "heavy rain and thunder", + "heavyrainshowers": "heavy rain showers", + "heavyrainshowersandthunder": "heavy rain showers and thunder", + "heavysleet": "heavy sleet", + "heavysleetandthunder": "heavy sleet and thunder", + "heavysleetshowers": "heavy sleet showers", + "heavysleetshowersandthunder": "heavy sleet showers and thunder", + "heavysnow": "heavy snow", + "heavysnowandthunder": "heavy snow and thunder", + "heavysnowshowers": "heavy snow showers", + "heavysnowshowersandthunder": "heavy snow showers and thunder", + "lightrain": "light rain", + "lightrainandthunder": "light rain and thunder", + "lightrainshowers": "light rain showers", + "lightrainshowersandthunder": "light rain showers and thunder", + "lightsleet": "light sleet", + "lightsleetandthunder": "light sleet and thunder", + "lightsleetshowers": "light sleet showers", + "lightsnow": "light snow", + "lightsnowandthunder": "light snow and thunder", + "lightsnowshowers": "light snow showers", + "lightssleetshowersandthunder": "light sleet showers and thunder", + "lightssnowshowersandthunder": "light snow showers and thunder", + "partlycloudy": "some clouds", + "rain": "rain", + "rainandthunder": "rain and thunder", + "rainshowers": "rain showers", + "rainshowersandthunder": "rain showers and thunder", + "sleet": "sleet", + "sleetandthunder": "sleet and thunder", + "sleetshowers": "sleet showers", + "sleetshowersandthunder": "sleet showers and thunder", + "snow": "snow", + "snowandthunder": "snow and thunder", + "snowshowers": "snow showers", + "snowshowersandthunder": "snow showers and thunder", +}; + export const getFormattedWeatherDescription = ( condition: string | undefined, ) => { - if (!condition) return; - - const weatherToEmoji: Record = { - "clear sky": "β˜€οΈ", - "few clouds": "🌀️", - "scattered clouds": "β›…", - "broken clouds": "☁️", - "overcast clouds": "☁️", - rain: "🌧️", - "light rain": "🌧️", - "moderate rain": "🌧️", - "heavy intensity rain": "🌧️", - "very heavy rain": "🌧️", - "extreme rain": "🌧️", - "freezing rain": "🌨️", - "light intensity shower rain": "🌦️", - "shower rain": "🌧️", - "heavy intensity shower rain": "🌧️", - "ragged shower rain": "🌧️", - "light snow": "❄️", - snow: "❄️", - "heavy snow": "❄️", - sleet: "🌨️", - "shower sleet": "🌨️", - "light rain and snow": "🌨️", - "rain and snow": "🌨️", - "light shower snow": "🌨️", - "shower snow": "🌨️", - "heavy shower snow": "🌨️", - mist: "🌫️", - smoke: "🌫️", - haze: "🌫️", - "sand/ dust whirls": "πŸŒͺ️", - fog: "🌫️", - sand: "🌫️", - dust: "🌫️", - "volcanic ash": "🌫️", - squalls: "🌬️", - tornado: "πŸŒͺ️", - clear: "β˜€οΈ", - clouds: "☁️", - }; - - return `${weatherToEmoji[condition.toLowerCase()] ?? ""} ${condition}`; + if (!condition || !weatherCodeToEnglish[condition]) return; + return `with ${weatherCodeToEnglish[condition]}`; };