diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..9444f81 --- /dev/null +++ b/.env.example @@ -0,0 +1,2 @@ +UPSTASH_REDIS_REQUEST_URL= +UPSTASH_REDIS_REST_TOKEN= diff --git a/bun.lockb b/bun.lockb index c0e30aa..05fa17f 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index bdde454..ef16ef6 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,8 @@ "vue-tsc": "^2.0.2" }, "dependencies": { + "@upstash/redis": "^1.28.4", + "date-fns": "^3.3.1", "nuxt": "^3.10.3", "vue": "^3.4.19", "vue-router": "^4.3.0" diff --git a/src/pages/index.vue b/src/pages/index.vue index 76885fd..a94c8b3 100644 --- a/src/pages/index.vue +++ b/src/pages/index.vue @@ -12,16 +12,7 @@ const backInTime = () => { return currentDate; }; -const { data } = useFetch( - () => - 'https://allertameteo.regione.emilia-romagna.it/o/api/allerta/get-sensor-values-no-time', - { - query: { - variabile: '254,0,0/1,-,-,-/B13215', - time: backInTime().getTime(), - }, - }, -); +const { data } = useFetch('/api/stations'); if (data) { time.value = data.value?.shift() as Time; diff --git a/src/server/api/stations.ts b/src/server/api/stations.ts index 2cf50c5..8ed4dbd 100644 --- a/src/server/api/stations.ts +++ b/src/server/api/stations.ts @@ -1,24 +1,58 @@ -const backInTime = () => { +import { addHours } from 'date-fns'; + +import { redis, keyParser, isCaching } from './../../utils/redis'; + +const currentHourUTCEpoch = () => { const currentDate = new Date(); - currentDate.setHours(currentDate.getHours() - 1); - currentDate.setMinutes(0); - currentDate.setSeconds(0); - currentDate.setMilliseconds(0); + if (currentDate.getMinutes() <= 30) { + currentDate.setMinutes(30, 0, 0); + } else { + addHours(currentDate, 1); + currentDate.setMinutes(0, 0, 0); + } - return currentDate; + return currentDate.getTime(); }; export default defineEventHandler(async (_event) => { - const data = await $fetch( - 'https://allertameteo.regione.emilia-romagna.it/o/api/allerta/get-sensor-values-no-time', - { - method: 'GET', - query: { - variabile: '254,0,0/1,-,-,-/B13215', - time: backInTime().getTime(), + // get the latest most complete stations data + const epoch = currentHourUTCEpoch().toString(); + + const fromCache = isCaching() + ? await redis.get(keyParser(epoch)) + : null; + if (!fromCache) { + console.log('Data not found in cache...'); + console.log('Fetching new data...'); + const data = await $fetch( + 'https://allertameteo.regione.emilia-romagna.it/o/api/allerta/get-sensor-values-no-time', + { + method: 'POST', + query: { + variabile: '254,0,0/1,-,-,-/B13215', + time: epoch, + }, + params: { + ts: epoch, + latestAvailabelTimeIsKnown: 'false', + time: epoch, + }, }, - }, - ); + ); + + if (isCaching()) { + // using transaction to optimize Redis server calls + const transaction = redis.multi(); + transaction.set(keyParser(epoch), JSON.stringify(data)); + transaction.expire(keyParser(epoch), 30 * 60); + + console.log('Saving new data to cache...'); + const _result = await transaction.exec(); + } - return data; + return data; + } else { + console.log('Data found in cache...'); + return fromCache; + } }); diff --git a/src/utils/redis.ts b/src/utils/redis.ts new file mode 100644 index 0000000..60e23d8 --- /dev/null +++ b/src/utils/redis.ts @@ -0,0 +1,29 @@ +import { Redis } from '@upstash/redis'; + +const UPSTASH_REDIS_REQUEST_URL = process.env.UPSTASH_REDIS_REQUEST_URL; +const UPSTASH_REDIS_REST_TOKEN = process.env.UPSTASH_REDIS_REST_TOKEN; +const REDIS_PREFIX = process.env.REDIS_PREFIX || 'LOCALDEV'; + +if (!UPSTASH_REDIS_REQUEST_URL) { + console.warn( + 'You have not configured `UPSTASH_REDIS_REQUEST_URL` env variable', + ); + console.warn('Request caching could not be enabled.'); +} + +if (!UPSTASH_REDIS_REST_TOKEN) { + console.warn( + 'You have not configured `UPSTASH_REDIS_REST_TOKEN` env variable.', + ); + console.warn('Request caching could not be enabled.'); +} + +export const isCaching = () => + UPSTASH_REDIS_REQUEST_URL && UPSTASH_REDIS_REST_TOKEN; + +export const redis = new Redis({ + url: UPSTASH_REDIS_REQUEST_URL || 'NO_URL', + token: UPSTASH_REDIS_REST_TOKEN || 'NO_TOKEN', +}); + +export const keyParser = (key: string) => `${key}_${REDIS_PREFIX}`;