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..8e1d0ba9 100644 --- a/src/app/(dashboard)/app/_components/weather.tsx +++ b/src/app/(dashboard)/app/_components/weather.tsx @@ -40,10 +40,10 @@ 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)} for today's + weather. ); }; 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..39642867 100644 --- a/src/utils/getFormattedWeatherDescription.ts +++ b/src/utils/getFormattedWeatherDescription.ts @@ -1,48 +1,52 @@ +const weatherCodeToEnglish: Record = { + clearsky: "🌞 a clear sky", + cloudy: "☁️ clouds", + fair: "β›… fair", + fair_day: "🌀️ fair day", + 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; + const weatherDescription = weatherCodeToEnglish[condition]; + return `with ${weatherDescription}`; };