feat: use Redis as simple cache for fetched articles

This commit is contained in:
Ayo Ayco 2025-08-07 17:31:15 +02:00
parent 5c17784ed5
commit 1c7ca35d85
4 changed files with 95 additions and 6 deletions

1
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1 @@
{}

View file

@ -34,6 +34,7 @@
"astro": "^5.12.8",
"astro-iconify": "^1.2.0",
"fastify": "^5.4.0",
"redis": "^5.8.0",
"ultrahtml": "^1.6.0"
},
"devDependencies": {

View file

@ -38,6 +38,9 @@ importers:
fastify:
specifier: ^5.4.0
version: 5.4.0
redis:
specifier: ^5.8.0
version: 5.8.0
ultrahtml:
specifier: ^1.6.0
version: 1.6.0
@ -855,6 +858,34 @@ packages:
resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==}
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
'@redis/bloom@5.8.0':
resolution: {integrity: sha512-kpKZzAAjGiGYn88Bqq6+ozxPg6kGYWRZH9vnOwGcoSCbrW14SZpZVMYMFSio8FH9ZJUdUcmT/RLGlA1W1t0UWQ==}
engines: {node: '>= 18'}
peerDependencies:
'@redis/client': ^5.8.0
'@redis/client@5.8.0':
resolution: {integrity: sha512-ywZjKGoSSAECGYOd9bJpws6d4867SN686obUWT/sRmo1c/Q8V+jWyInvlqwKa0BOvTHHwYeB2WFUEvd6PADeOQ==}
engines: {node: '>= 18'}
'@redis/json@5.8.0':
resolution: {integrity: sha512-xPBpwY6aKoRzMSu67MpwrBiSliON9bfHo9Y/pSPBjW8/KoOm1MzGqwJUO20qdjXpFoKJsDWwxIE1LHdBNzcImw==}
engines: {node: '>= 18'}
peerDependencies:
'@redis/client': ^5.8.0
'@redis/search@5.8.0':
resolution: {integrity: sha512-lF9pNv9vySmirm1EzCH6YeRjhvH6lLT7tvebYHEM7WTkEQ/7kZWb4athliKESHpxzTQ36U9UbzuedSywHV6OhQ==}
engines: {node: '>= 18'}
peerDependencies:
'@redis/client': ^5.8.0
'@redis/time-series@5.8.0':
resolution: {integrity: sha512-kPTlW2ACXokjQNXjCD8Pw9mHDoB94AHUlHFahyjxz9lUJUVwiva2Dgfrd2k3JxHhSBqyY2PREIj9YwIUSTSSqQ==}
engines: {node: '>= 18'}
peerDependencies:
'@redis/client': ^5.8.0
'@rollup/pluginutils@5.2.0':
resolution: {integrity: sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==}
engines: {node: '>=14.0.0'}
@ -1431,6 +1462,10 @@ packages:
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
engines: {node: '>=6'}
cluster-key-slot@1.1.2:
resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==}
engines: {node: '>=0.10.0'}
color-convert@2.0.1:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'}
@ -2972,6 +3007,10 @@ packages:
resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==}
engines: {node: '>= 12.13.0'}
redis@5.8.0:
resolution: {integrity: sha512-re0MHm1KHbiVIUPDGoUM3jldvjH5EM/wGZ3A33gyUYoC/UnVNKNnZHM5hcJVry7L2O2eJU3nflSXTliv10BTKg==}
engines: {node: '>= 18'}
reflect.getprototypeof@1.0.10:
resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==}
engines: {node: '>= 0.4'}
@ -4592,6 +4631,26 @@ snapshots:
'@pkgr/core@0.2.9': {}
'@redis/bloom@5.8.0(@redis/client@5.8.0)':
dependencies:
'@redis/client': 5.8.0
'@redis/client@5.8.0':
dependencies:
cluster-key-slot: 1.1.2
'@redis/json@5.8.0(@redis/client@5.8.0)':
dependencies:
'@redis/client': 5.8.0
'@redis/search@5.8.0(@redis/client@5.8.0)':
dependencies:
'@redis/client': 5.8.0
'@redis/time-series@5.8.0(@redis/client@5.8.0)':
dependencies:
'@redis/client': 5.8.0
'@rollup/pluginutils@5.2.0(rollup@4.46.2)':
dependencies:
'@types/estree': 1.0.8
@ -5343,6 +5402,8 @@ snapshots:
clsx@2.1.1: {}
cluster-key-slot@1.1.2: {}
color-convert@2.0.1:
dependencies:
color-name: 1.1.4
@ -7243,6 +7304,14 @@ snapshots:
real-require@0.2.0: {}
redis@5.8.0:
dependencies:
'@redis/bloom': 5.8.0(@redis/client@5.8.0)
'@redis/client': 5.8.0
'@redis/json': 5.8.0(@redis/client@5.8.0)
'@redis/search': 5.8.0(@redis/client@5.8.0)
'@redis/time-series': 5.8.0(@redis/client@5.8.0)
reflect.getprototypeof@1.0.10:
dependencies:
call-bind: 1.0.8

View file

@ -1,11 +1,16 @@
---
import { ArticleData, extract } from '@extractus/article-extractor'
import { createClient } from 'redis'
import { type ArticleData, extract } from '@extractus/article-extractor'
import SimpleAddressBar from '../components/SimpleAddressBar.astro'
import Post from '../components/Post.astro'
import App from '../layouts/App.astro'
import Library from '../components/Library.astro'
import Footer from '../components/Footer.astro'
const client = createClient()
// TODO: put on a log file
client.on('error', (err) => console.error('Redis Client Error', err))
await client.connect()
export const prerender = false
let url = Astro.url.searchParams.get('url')
@ -15,12 +20,25 @@ while (url?.startsWith(Astro.url.origin)) {
url = new URL(url).searchParams.get('url')
}
if (url)
if (url) {
// try cache
article = (await client.json.get(url)) as ArticleData
if (article !== null) {
console.log('>>> Using cached content', article.url)
} else {
try {
article = await extract(url)
console.log('>>> Using fetched content', article?.url)
if (article !== null) {
// cache via redis
await client.json.set(url, '$', article)
console.log('>>> Added to cache', article.url)
}
} catch {
article = null
}
}
}
---
<App article={article}>