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": "^5.12.8",
"astro-iconify": "^1.2.0", "astro-iconify": "^1.2.0",
"fastify": "^5.4.0", "fastify": "^5.4.0",
"redis": "^5.8.0",
"ultrahtml": "^1.6.0" "ultrahtml": "^1.6.0"
}, },
"devDependencies": { "devDependencies": {

View file

@ -38,6 +38,9 @@ importers:
fastify: fastify:
specifier: ^5.4.0 specifier: ^5.4.0
version: 5.4.0 version: 5.4.0
redis:
specifier: ^5.8.0
version: 5.8.0
ultrahtml: ultrahtml:
specifier: ^1.6.0 specifier: ^1.6.0
version: 1.6.0 version: 1.6.0
@ -855,6 +858,34 @@ packages:
resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==}
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} 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': '@rollup/pluginutils@5.2.0':
resolution: {integrity: sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==} resolution: {integrity: sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==}
engines: {node: '>=14.0.0'} engines: {node: '>=14.0.0'}
@ -1431,6 +1462,10 @@ packages:
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
engines: {node: '>=6'} 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: color-convert@2.0.1:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'} engines: {node: '>=7.0.0'}
@ -2972,6 +3007,10 @@ packages:
resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==}
engines: {node: '>= 12.13.0'} engines: {node: '>= 12.13.0'}
redis@5.8.0:
resolution: {integrity: sha512-re0MHm1KHbiVIUPDGoUM3jldvjH5EM/wGZ3A33gyUYoC/UnVNKNnZHM5hcJVry7L2O2eJU3nflSXTliv10BTKg==}
engines: {node: '>= 18'}
reflect.getprototypeof@1.0.10: reflect.getprototypeof@1.0.10:
resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@ -4592,6 +4631,26 @@ snapshots:
'@pkgr/core@0.2.9': {} '@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)': '@rollup/pluginutils@5.2.0(rollup@4.46.2)':
dependencies: dependencies:
'@types/estree': 1.0.8 '@types/estree': 1.0.8
@ -5343,6 +5402,8 @@ snapshots:
clsx@2.1.1: {} clsx@2.1.1: {}
cluster-key-slot@1.1.2: {}
color-convert@2.0.1: color-convert@2.0.1:
dependencies: dependencies:
color-name: 1.1.4 color-name: 1.1.4
@ -7243,6 +7304,14 @@ snapshots:
real-require@0.2.0: {} 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: reflect.getprototypeof@1.0.10:
dependencies: dependencies:
call-bind: 1.0.8 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 SimpleAddressBar from '../components/SimpleAddressBar.astro'
import Post from '../components/Post.astro' import Post from '../components/Post.astro'
import App from '../layouts/App.astro' import App from '../layouts/App.astro'
import Library from '../components/Library.astro' import Library from '../components/Library.astro'
import Footer from '../components/Footer.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 export const prerender = false
let url = Astro.url.searchParams.get('url') let url = Astro.url.searchParams.get('url')
@ -15,12 +20,25 @@ while (url?.startsWith(Astro.url.origin)) {
url = new URL(url).searchParams.get('url') url = new URL(url).searchParams.get('url')
} }
if (url) if (url) {
try { // try cache
article = await extract(url) article = (await client.json.get(url)) as ArticleData
} catch { if (article !== null) {
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}> <App article={article}>