@@ -27,10 +27,19 @@ const IGNORED_URLS = ["/customer", "/media", "/admin", "/checkout"];
2727const HTTPS = getEnvBoolean ( "HTTPS" , true ) ;
2828const HOST = process . env . HOST || false ;
2929const MINIFY = getEnvBoolean ( "MINIFY" ) ;
30+ const USE_STALE = getEnvBoolean ( "USE_STALE" , true ) ;
3031
3132// APCu Equivalent (Node.js in-memory cache)
3233const cache = new NodeCache ( { stdTTL : CACHE_TTL } ) ; // 5 min cache
3334
35+ if ( USE_STALE ) {
36+ cache . on ( "del" /*expired*/ , ( key , value ) => {
37+ console . log ( "EXPIRED:" + key ) ;
38+ value . expired = true ;
39+ cache . set ( key , value , CACHE_TTL * 10 ) ;
40+ } ) ;
41+ }
42+
3443// Start HTTP Server
3544const server = http . createServer ( async ( req , res ) => {
3645 const startTime = process . hrtime ( ) ;
@@ -51,7 +60,23 @@ const server = http.createServer(async (req, res) => {
5160 }
5261
5362 // Try APCu like (NodeCache) First
63+ var cacheInfo = null ;
5464 let cachedPage = USE_CACHE ? cache . get ( cacheKey ) : null ;
65+ if ( cachedPage ) {
66+ cacheInfo = getCacheInfo ( cacheKey ) ;
67+ res . setHeader ( "Node-Cache" , "true" ) ;
68+ console . log ( 'NodeCACHE: HIT' ) ;
69+ } else if ( USE_STALE ) {
70+ // when cache expired we need check stale twice
71+ cachedPage = cache . get ( cacheKey )
72+ if ( cachedPage ) {
73+ cacheInfo = getCacheInfo ( cacheKey ) ;
74+ res . setHeader ( "Node-Stale" , "true" ) ;
75+ console . log ( 'NodeCACHE: STALE' ) ;
76+ } else {
77+ console . log ( 'NodeCACHE: MISS' ) ;
78+ }
79+ }
5580
5681 if ( ! cachedPage ) {
5782 const redisStartTime = process . hrtime ( ) ;
@@ -60,14 +85,11 @@ const server = http.createServer(async (req, res) => {
6085 const redisTimeMs = ( redisEndTime [ 1 ] / 1e6 ) . toFixed ( 2 ) ;
6186 if ( DEBUG ) res . setHeader ( "Server-Timing" , `redis;dur=${ redisTimeMs } ` ) ;
6287
63-
6488 if ( ! cachedPage ) {
6589 if ( DEBUG ) res . setHeader ( "Fast-Cache" , "MISS" ) ;
6690 return sendNotFound ( res ) ;
6791 }
6892
69- cachedPage = await uncompress ( cachedPage ) ;
70- cachedPage = JSON . parse ( cachedPage ) ;
7193 if ( USE_CACHE ) cache . set ( cacheKey , cachedPage , CACHE_TTL ) ;
7294 } else {
7395 if ( DEBUG ) res . setHeader ( "Fast-Cache" , "HIT (NodeCACHE)" ) ;
@@ -82,11 +104,13 @@ const server = http.createServer(async (req, res) => {
82104
83105 let content = cachedPage . content ;
84106 if ( MINIFY && ! cachedPage . minified ) {
107+ ( async ( ) => {
85108 content = await minifyHTML ( content ) ;
86- cachedPage . content = content ;
87- cachedPage . minified = true ;
88- if ( USE_CACHE ) cache . set ( cacheKey , cachedPage , CACHE_TTL ) ;
89- //ToDo: resave minified to Redis ;)
109+ cachedPage . content = content ;
110+ cachedPage . minified = true ;
111+ if ( USE_CACHE ) cache . set ( cacheKey , cachedPage , CACHE_TTL ) ;
112+ //ToDo: resave minified to Redis ;)
113+ } ) ( ) ;
90114 }
91115
92116 // Measure Total Execution Time
@@ -97,10 +121,30 @@ const server = http.createServer(async (req, res) => {
97121 console . log ( "FPC-TIME:[" + req . url + "]->" + ( endTime [ 1 ] / 1e6 ) . toFixed ( 2 ) + "ms" ) ;
98122
99123 res . writeHead ( 200 , { "Content-Type" : "text/html" } ) ;
124+
125+ if ( USE_STALE && cacheInfo !== null && cacheInfo . stale ) {
126+ ( async ( ) => {
127+ try {
128+ console . log ( 'Fetched new data' ) ;
129+ const newContent = await fetchOriginalData ( req , { 'Refresh' : '1' } ) ;
130+ if ( newContent ) {
131+ let oldCache = cache . get ( cacheKey ) ;
132+ let newCache = await getRedisValue ( cacheKey , "d" ) ;
133+ if ( oldCache && newCache ) {
134+ // Update the cache with new data
135+ console . log ( 'SET new data' ) ;
136+ cache . set ( cacheKey , newCache , CACHE_TTL ) ;
137+ }
138+ }
139+ } catch ( error ) {
140+ console . error ( 'Error fetching new data:' , error ) ;
141+ }
142+ } ) ( ) ;
143+ }
144+ console . log ( '----Response Return---' ) ;
100145 res . end ( content ) ;
101146 } catch ( err ) {
102147 if ( DEBUG ) {
103- res . setHeader ( "FPC-ERROR" , "Exception" ) ;
104148 console . error ( "FPC Error:" , err ) ;
105149 }
106150 sendNotFound ( res ) ;
@@ -110,6 +154,8 @@ const server = http.createServer(async (req, res) => {
110154// Function to Check if Request is Cacheable
111155function isCached ( req ) {
112156 if ( req . method !== "GET" ) return false ;
157+ // Bypass cache for refresh requests
158+ if ( req . headers [ "refresh" ] === "1" ) return false ;
113159 return ! IGNORED_URLS . some ( url => req . url . startsWith ( url ) ) ;
114160}
115161
@@ -163,10 +209,16 @@ function sendNotFound(res) {
163209 res . end ( JSON . stringify ( { error : "Not Cached" } ) ) ;
164210}
165211
166- async function getRedisValue ( key , field ) {
212+ async function getRedisValue ( key , field = "d" ) {
167213 try {
168- const value = await redis . hget ( key , field ) ; // Use HGET instead of HGETALL
169- // console.log("HGET Response:", JSON.parse(value));
214+ let value = await redis . hget ( key , field ) ; // Use HGET instead of HGETALL
215+ console . log ( "HGET:" , Boolean ( value ) ) ;
216+ if ( ! value ) {
217+ return false ;
218+ }
219+
220+ value = await uncompress ( value ) ;
221+ value = JSON . parse ( value ) ;
170222 return value ;
171223 } catch ( err ) {
172224 console . error ( "Redis Error:" , err ) ;
@@ -212,6 +264,44 @@ async function minifyHTML(htmlContent) {
212264 } ) ;
213265}
214266
267+ // Function to get the TTL and saved time of a cached object
268+ function getCacheInfo ( key ) {
269+ const now = Date . now ( ) ;
270+ const ttl = cache . getTtl ( key ) ; // Get the TTL of the key timestamp when expired
271+ let stale = false ;
272+ if ( cache . get ( key ) . expired ) {
273+ stale = true ;
274+ }
275+
276+ if ( ttl !== undefined ) {
277+ console . log ( `Key: ${ key } ` ) ;
278+ console . log ( `TTL: ${ ttl } ms` ) ;
279+ console . log ( `Expired:` , stale ) ;
280+ } else {
281+ //console.log(`Key: ${key} not found in cache.`);
282+ }
283+ return { stale, ttl}
284+ }
285+
286+ // Function to fetch original data with additional headers
287+ async function fetchOriginalData ( req , additionalHeaders = { } ) {
288+ try {
289+ const url = getUrl ( req ) ;
290+ const originalHeaders = req . headers ;
291+ // Merge original headers with additional headers
292+ const headers = { ...originalHeaders , ...additionalHeaders } ;
293+
294+ const response = await fetch ( url , { headers } ) ;
295+ if ( ! response . ok ) {
296+ return false ;
297+ }
298+ return response ;
299+ } catch ( error ) {
300+ console . error ( 'Error fetching data:' , error ) ;
301+ return null ;
302+ }
303+ }
304+
215305// Start Server
216306const PORT = process . env . PORT || 3001 ;
217307server . listen ( PORT , ( ) => console . log ( `🚀 Node.js FPC Server running on port ${ PORT } ` ) ) ;
0 commit comments