feat(clients/weather): in-memory cache
This commit is contained in:
parent
5c0a84b3eb
commit
2e029e47c0
3 changed files with 61 additions and 22 deletions
|
|
@ -26,6 +26,7 @@
|
||||||
"eslint": "^9.39.1",
|
"eslint": "^9.39.1",
|
||||||
"eslint-plugin-svelte": "^3.13.0",
|
"eslint-plugin-svelte": "^3.13.0",
|
||||||
"globals": "^16.5.0",
|
"globals": "^16.5.0",
|
||||||
|
"node-cache": "^5.1.2",
|
||||||
"svelte": "^5.43.8",
|
"svelte": "^5.43.8",
|
||||||
"svelte-check": "^4.3.4",
|
"svelte-check": "^4.3.4",
|
||||||
"tailwindcss": "^4.1.17",
|
"tailwindcss": "^4.1.17",
|
||||||
|
|
|
||||||
17
pnpm-lock.yaml
generated
17
pnpm-lock.yaml
generated
|
|
@ -47,6 +47,9 @@ importers:
|
||||||
globals:
|
globals:
|
||||||
specifier: ^16.5.0
|
specifier: ^16.5.0
|
||||||
version: 16.5.0
|
version: 16.5.0
|
||||||
|
node-cache:
|
||||||
|
specifier: ^5.1.2
|
||||||
|
version: 5.1.2
|
||||||
svelte:
|
svelte:
|
||||||
specifier: ^5.43.8
|
specifier: ^5.43.8
|
||||||
version: 5.45.2
|
version: 5.45.2
|
||||||
|
|
@ -679,6 +682,10 @@ packages:
|
||||||
class-variance-authority@0.7.1:
|
class-variance-authority@0.7.1:
|
||||||
resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==}
|
resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==}
|
||||||
|
|
||||||
|
clone@2.1.2:
|
||||||
|
resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==}
|
||||||
|
engines: {node: '>=0.8'}
|
||||||
|
|
||||||
clsx@2.1.1:
|
clsx@2.1.1:
|
||||||
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
|
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
@ -1034,6 +1041,10 @@ packages:
|
||||||
natural-compare@1.4.0:
|
natural-compare@1.4.0:
|
||||||
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
|
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
|
||||||
|
|
||||||
|
node-cache@5.1.2:
|
||||||
|
resolution: {integrity: sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==}
|
||||||
|
engines: {node: '>= 8.0.0'}
|
||||||
|
|
||||||
optionator@0.9.4:
|
optionator@0.9.4:
|
||||||
resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
|
resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
|
||||||
engines: {node: '>= 0.8.0'}
|
engines: {node: '>= 0.8.0'}
|
||||||
|
|
@ -1801,6 +1812,8 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
clsx: 2.1.1
|
clsx: 2.1.1
|
||||||
|
|
||||||
|
clone@2.1.2: {}
|
||||||
|
|
||||||
clsx@2.1.1: {}
|
clsx@2.1.1: {}
|
||||||
|
|
||||||
color-convert@2.0.1:
|
color-convert@2.0.1:
|
||||||
|
|
@ -2133,6 +2146,10 @@ snapshots:
|
||||||
|
|
||||||
natural-compare@1.4.0: {}
|
natural-compare@1.4.0: {}
|
||||||
|
|
||||||
|
node-cache@5.1.2:
|
||||||
|
dependencies:
|
||||||
|
clone: 2.1.2
|
||||||
|
|
||||||
optionator@0.9.4:
|
optionator@0.9.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
deep-is: 0.1.4
|
deep-is: 0.1.4
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import NodeCache from "node-cache";
|
||||||
import crypto from "node:crypto";
|
import crypto from "node:crypto";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -10,6 +11,14 @@ const USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
|
||||||
*/
|
*/
|
||||||
const WEATHER_TOKEN_SECRET = "gravisfrontem";
|
const WEATHER_TOKEN_SECRET = "gravisfrontem";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response cache.
|
||||||
|
*/
|
||||||
|
const cache = new NodeCache({
|
||||||
|
// NOTE: 10 minutes in a seconds
|
||||||
|
stdTTL: 60 * 10,
|
||||||
|
});
|
||||||
|
|
||||||
interface YandexWeatherData {
|
interface YandexWeatherData {
|
||||||
/**
|
/**
|
||||||
* Message from the server.
|
* Message from the server.
|
||||||
|
|
@ -18,54 +27,54 @@ interface YandexWeatherData {
|
||||||
|
|
||||||
info: {
|
info: {
|
||||||
/**
|
/**
|
||||||
* URL of the location page on Yandex Weather
|
* URL of the location page on Yandex Weather.
|
||||||
**/
|
**/
|
||||||
url: string;
|
url: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
fact: {
|
fact: {
|
||||||
/**
|
/**
|
||||||
* Current air temperature in °C
|
* Current air temperature in °C.
|
||||||
**/
|
**/
|
||||||
temp: number;
|
temp: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Water temperature in °C (may be missing)
|
* Water temperature in °C.
|
||||||
**/
|
**/
|
||||||
temp_water?: number;
|
temp_water?: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perceived (“feels like”) temperature in °C
|
* Perceived (“feels like”) temperature in °C.
|
||||||
**/
|
**/
|
||||||
feels_like: number;
|
feels_like: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Weather condition code (e.g., "clear", "rain")
|
* Weather condition code (e.g., "clear", "rain").
|
||||||
**/
|
**/
|
||||||
condition: string;
|
condition: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wind speed in m/s
|
* Wind speed in m/s.
|
||||||
**/
|
**/
|
||||||
wind_speed: number;
|
wind_speed: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wind gust speed in m/s (may be missing)
|
* Wind gust speed in m/s.
|
||||||
**/
|
**/
|
||||||
wind_gust?: number;
|
wind_gust?: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wind direction code (e.g., "nw", "s")
|
* Wind direction.
|
||||||
**/
|
**/
|
||||||
wind_dir: string;
|
wind_dir: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Atmospheric pressure in mmHg
|
* Atmospheric pressure in mmHg.
|
||||||
**/
|
**/
|
||||||
pressure_mm: number;
|
pressure_mm: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Air humidity percentage
|
* Air humidity percentage.
|
||||||
**/
|
**/
|
||||||
humidity: number;
|
humidity: number;
|
||||||
};
|
};
|
||||||
|
|
@ -111,7 +120,7 @@ export interface FetchWeatherResult {
|
||||||
gust?: number;
|
gust?: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wind direction code.
|
* Wind direction.
|
||||||
**/
|
**/
|
||||||
direction: string;
|
direction: string;
|
||||||
};
|
};
|
||||||
|
|
@ -140,6 +149,10 @@ function getWeatherHeaders() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function fetchWeather(lat: number, lon: number): Promise<FetchWeatherResult> {
|
export default async function fetchWeather(lat: number, lon: number): Promise<FetchWeatherResult> {
|
||||||
|
const key = `${lat};${lon}`
|
||||||
|
|
||||||
|
let result: YandexWeatherData | undefined = cache.get(key);
|
||||||
|
if (!result) {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`https://api.weather.yandex.ru/v2/informers?lat=${lat}&lon=${lon}&lang=en_US`,
|
`https://api.weather.yandex.ru/v2/informers?lat=${lat}&lon=${lon}&lang=en_US`,
|
||||||
{
|
{
|
||||||
|
|
@ -150,10 +163,18 @@ export default async function fetchWeather(lat: number, lon: number): Promise<Fe
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const result: YandexWeatherData = await response.json();
|
result = await response.json();
|
||||||
|
|
||||||
|
// NOTE: to fix with typescript errors
|
||||||
|
if (!result) {
|
||||||
|
throw new Error('Result is undefined')
|
||||||
|
}
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`Yandex Weather API error: ${result.message}`)
|
throw new Error(result.message!)
|
||||||
|
}
|
||||||
|
|
||||||
|
cache.set(key, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue