feat: create logger; update cached articles on load

This commit is contained in:
Ayo Ayco 2024-08-16 08:47:31 +02:00
parent ed2364ebe8
commit 4daa099b22
5 changed files with 108 additions and 28 deletions

View file

@ -17,7 +17,9 @@ export interface Props {
import type { AppConfig } from '../pages/index.astro'; import type { AppConfig } from '../pages/index.astro';
import { getPostCard, renderPost } from '../utils/library' import { getPostCard, renderPost } from '../utils/library'
import { cozify } from '../utils/sanitizer'; import { cozify } from '../utils/sanitizer';
import { logError, logInfo } from '../utils/logger.mjs';
const cache = await caches.open('cozy-reader'); const cache = await caches.open('cozy-reader');
const baseUrl = window.location.origin;
let url= new URL(window.location.href); let url= new URL(window.location.href);
// only cached unencoded url param // only cached unencoded url param
const urlParam = url.searchParams.get('url') const urlParam = url.searchParams.get('url')
@ -26,16 +28,15 @@ export interface Props {
} }
const { skipSave } = deserialize<Props>('preferences'); const { skipSave } = deserialize<Props>('preferences');
const { routerOutlet } = deserialize<AppConfig>('app-config'); const { routerOutlet } = deserialize<AppConfig>('app-config');
const includesAppURL = urlParam?.includes(window.location.origin) const includesAppURL = urlParam?.includes(baseUrl) ?? false;
try { try {
const responseFromNetwork = await fetch (url); if (url.href.slice(0, url.href.length - 1) !== baseUrl && !skipSave && !includesAppURL) {
if (responseFromNetwork && !skipSave && !includesAppURL) { logInfo('adding one to cache', {context: 'cozy-reader', data: url})
console.info('[cozy-reader]: adding one to cache', url.pathname) await cache.add(url);
await cache.put(url, responseFromNetwork);
} }
} catch(error) { } catch(error) {
console.error('[cozy-reader]: ', error) logError('ERR', {context: 'cozy-reader', data: error})
} }
const cachedRequests = (await cache.keys()) const cachedRequests = (await cache.keys())
@ -44,7 +45,7 @@ export interface Props {
const urlParam = urlObj.searchParams.get('url'); const urlParam = urlObj.searchParams.get('url');
return urlObj.search !== '' return urlObj.search !== ''
&& !urlParam?.startsWith(window.location.origin) && !urlParam?.startsWith(baseUrl)
&& urlParam !== '' && urlParam !== ''
&& urlParam !== 'null'; && urlParam !== 'null';
}); });
@ -63,9 +64,19 @@ export interface Props {
let responseText; let responseText;
const fullResponse = await cache.match(url) const fullResponse = await cache.match(url)
try {
const responseFromNetwork = await fetch(url, {method: 'GET'});
if (responseFromNetwork && url.slice(0, url.length - 1) !== baseUrl && !skipSave && !includesAppURL) {
logInfo('updating cached', {context: 'cozy-reader', data: url})
await cache.put(url, responseFromNetwork);
}
} catch(error) {
logError('failed to update cached', {context: 'cozy-reader', data: {url, error}})
}
fullResponse?.text().then(async data => { fullResponse?.text().then(async data => {
responseText = data; responseText = data;
const { baseUrl } = deserialize<AppConfig>('app-config');
const cleanedResponse = await cozify(responseText, baseUrl) const cleanedResponse = await cozify(responseText, baseUrl)
const html = document.createElement('html'); const html = document.createElement('html');
html.innerHTML = cleanedResponse; html.innerHTML = cleanedResponse;
@ -82,6 +93,7 @@ export interface Props {
e.preventDefault(); e.preventDefault();
localStorage.setItem('scrollPosition', window.scrollY.toString()); localStorage.setItem('scrollPosition', window.scrollY.toString());
scrollTo(0,0); scrollTo(0,0);
logInfo('using cached response', {context: 'cozy-reader', data: url})
renderPost(cleanedResponse, url, routerOutlet) renderPost(cleanedResponse, url, routerOutlet)
} }
const item = document.createElement('li'); const item = document.createElement('li');
@ -105,8 +117,8 @@ export interface Props {
const fullResponse = await cache.match(url) const fullResponse = await cache.match(url)
fullResponse?.text().then(async (data) => { fullResponse?.text().then(async (data) => {
const responseText = data; const responseText = data;
const { baseUrl } = deserialize<AppConfig>('app-config');
const cleanedResponse = await cozify(responseText, baseUrl); const cleanedResponse = await cozify(responseText, baseUrl);
logInfo('using cached response', {context: 'cozy-reader', data: url})
renderPost(cleanedResponse, url, routerOutlet, true); renderPost(cleanedResponse, url, routerOutlet, true);
if (isHome) { if (isHome) {
const scrollPosition = localStorage.getItem('scrollPosition'); const scrollPosition = localStorage.getItem('scrollPosition');

View file

@ -4,4 +4,4 @@ export const SITE_AUTHOR_MASTODON = 'https://social.ayco.io/@ayo';
export const SITE_PROJECT_REPO = 'https://github.com/ayoayco/Cozy'; export const SITE_PROJECT_REPO = 'https://github.com/ayoayco/Cozy';
export const SITE_DESCRIPTION = 'The Web is Yours.'; export const SITE_DESCRIPTION = 'The Web is Yours.';
export const VERSION = 'Purple-Pizza'; export const VERSION = 'Purple-Pizza';

View file

@ -11,7 +11,6 @@ export const prerender = false;
const appConfig = { const appConfig = {
routerOutlet: 'router-outlet', routerOutlet: 'router-outlet',
baseUrl: Astro.url.origin
}; };
export type AppConfig = typeof appConfig; export type AppConfig = typeof appConfig;

View file

@ -4,20 +4,35 @@
* @see https://ayco.io/n/@ayco/astro-sw * @see https://ayco.io/n/@ayco/astro-sw
*/ */
const cacheName = `${__prefix ?? 'app'}-v${__version ?? '000'}` const cacheName = `${__prefix ?? 'app'}-v${__version ?? '000'}`
// const forceLogging = true;
/**
* TODO: remove this once astro-sw allows importing utils
*/
function logInfo(message, {context, force, data} = {}) {
context = context !== ''
? `[${context}]: `
: ''
if (force || isDev) {
console.info(`!!! ${context}${message}`, data ?? '');
}
}
const addResourcesToCache = async (resources) => { const addResourcesToCache = async (resources) => {
const cache = await caches.open(cacheName); const cache = await caches.open(cacheName);
console.log('[cozy-sw]: adding resources to cache...', resources) logInfo('adding resources to cache...', { force: forceLogging, context: 'cozy-sw', data: resources })
await cache.addAll(resources); await cache.addAll(resources);
}; };
const putInCache = async (request, response) => { const putInCache = async (request, response) => {
const cache = await caches.open(cacheName); const cache = await caches.open(cacheName);
console.log('[cozy-sw]: adding one response to cache...', request.url) logInfo('adding one response to cache...', { force: forceLogging, context: 'cozy-sw', data: request.url })
// if exists, replace // if exists, replace
const keys = await cache.keys(); const keys = await cache.keys();
if(keys.includes(request)) { if (keys.includes(request)) {
cache.delete(request); cache.delete(request);
} }
@ -26,7 +41,7 @@ const putInCache = async (request, response) => {
const cacheAndRevalidate = async ({ request, preloadResponsePromise, fallbackUrl }) => { const cacheAndRevalidate = async ({ request, preloadResponsePromise, fallbackUrl }) => {
const cache = await caches.open(cacheName); const cache = await caches.open(cacheName);
// Try get the resource from the cache // Try get the resource from the cache
@ -35,21 +50,21 @@ const cacheAndRevalidate = async ({ request, preloadResponsePromise, fallbackUrl
// get network response for revalidation of stale assets // get network response for revalidation of stale assets
const responseFromNetwork = await fetch(request.clone()); const responseFromNetwork = await fetch(request.clone());
if (responseFromNetwork) { if (responseFromNetwork) {
console.info('[cozy-sw]: fetched updated assets', responseFromNetwork.url); logInfo('updated cached resource...', { force: forceLogging, context: 'cozy-sw', data: responseFromNetwork.url })
putInCache(request, responseFromNetwork.clone()); putInCache(request, responseFromNetwork.clone());
} }
if (responseFromCache) { if (responseFromCache) {
console.info('[cozy-sw]: using cached response', responseFromCache.url); logInfo('using cached response...', { force: forceLogging, context: 'cozy-sw', data: responseFromCache.url })
return responseFromCache; return responseFromCache;
} else{ } else {
console.info('[cozy-sw]: using network response', responseFromNetwork.url); logInfo('using network response...', { force: forceLogging, context: 'cozy-sw', data: responseFromNetwork.url })
return responseFromNetwork; return responseFromNetwork;
} }
} catch(error) { } catch (error) {
console.info('[cozy-sw]: failed to fetch updated assets', request.url); logInfo('failed to fetch updated resource', { force: forceLogging, context: 'cozy-sw', data: request.url })
if (responseFromCache) { if (responseFromCache) {
console.info('[cozy-sw]: using cached response', responseFromCache.url); logInfo('using cached response', { force: forceLogging, context: 'cozy-sw', data: responseFromCache.url })
return responseFromCache; return responseFromCache;
} }
} }
@ -63,18 +78,18 @@ const cacheAndRevalidate = async ({ request, preloadResponsePromise, fallbackUrl
const preloadResponse = await preloadResponsePromise; const preloadResponse = await preloadResponsePromise;
if (preloadResponse) { if (preloadResponse) {
putInCache(request, preloadResponse.clone()); putInCache(request, preloadResponse.clone());
console.info('[cozy-sw]: using preload response', preloadResponse.url); logInfo('using preload response', { force: forceLogging, context: 'cozy-sw', data: preloadResponse.url })
return preloadResponse; return preloadResponse;
} }
try { try {
// Try to get the resource from the network for 5 seconds // Try to get the resource from the network for 5 seconds
const responseFromNetwork = await fetch(request.clone(), {signal: AbortSignal.timeout(5000)}); const responseFromNetwork = await fetch(request.clone(), { signal: AbortSignal.timeout(5000) });
// response may be used only once // response may be used only once
// we need to save clone to put one copy in cache // we need to save clone to put one copy in cache
// and serve second one // and serve second one
putInCache(request, responseFromNetwork.clone()); putInCache(request, responseFromNetwork.clone());
console.info('[cozy-sw]: using network response', responseFromNetwork.url); logInfo('using network response', { force: forceLogging, context: 'cozy-sw', data: responseFromNetwork.url })
return responseFromNetwork; return responseFromNetwork;
} catch (error) { } catch (error) {
@ -82,7 +97,7 @@ const cacheAndRevalidate = async ({ request, preloadResponsePromise, fallbackUrl
// Try the fallback // Try the fallback
const fallbackResponse = await cache.match(fallbackUrl); const fallbackResponse = await cache.match(fallbackUrl);
if (fallbackResponse) { if (fallbackResponse) {
console.info('[cozy-sw]: using fallback cached response', fallbackResponse.url); logInfo('using fallback cached response...', { force: forceLogging, context: 'cozy-sw', data: fallbackResponse.url })
return fallbackResponse; return fallbackResponse;
} }
@ -104,12 +119,12 @@ const enableNavigationPreload = async () => {
}; };
self.addEventListener('activate', (event) => { self.addEventListener('activate', (event) => {
console.log('[cozy-sw]: activating...', event) logInfo('activating service worker...', { force: forceLogging, context: 'cozy-sw' })
event.waitUntil(enableNavigationPreload()); event.waitUntil(enableNavigationPreload());
}); });
self.addEventListener('install', (event) => { self.addEventListener('install', (event) => {
console.log('[cozy-sw]: installing...', event) logInfo('installing service worker...', { force: forceLogging, context: 'cozy-sw' })
event.waitUntil( event.waitUntil(
addResourcesToCache([ addResourcesToCache([
...(__assets ?? []) ...(__assets ?? [])

54
src/utils/logger.mjs Normal file
View file

@ -0,0 +1,54 @@
// @ts-check
const isDev = import.meta.env.DEV;
/**
* @typedef {{
* force?: true
* context?: string,
* data?: any
* }} LogOptions
*/
/**
* @param {string} message
* @param {LogOptions} options
*/
export function logMessage(message, {context, force, data} = {}) {
context = context !== ''
? `[${context}]: `
: ''
if (force || isDev) {
console.log(`!!! ${context}${message}`, data ?? '');
}
}
/**
* @param {string} message
* @param {LogOptions} options
*/
export function logInfo(message, {context, force, data} = {}) {
context = context !== ''
? `[${context}]: `
: ''
if (force || isDev) {
console.info(`!!! ${context}${message}`, data ?? '');
}
}
/**
* @param {string} message
* @param {LogOptions} options
*/
export function logError(message, {context, force, data} = {}) {
context = context !== ''
? `[${context}]: `
: ''
if (force || isDev) {
console.error(`!!! ${context}${message}`, data ?? '');
}
}