From 89bfb4d952051091cfc7c85ff982b0e865a3b1ff Mon Sep 17 00:00:00 2001 From: shigahi Date: Thu, 22 Jan 2026 23:48:58 +0100 Subject: [PATCH] Add Cache-Control header optimization - Add CachingOptions interface with htmlTtl, staticAssetsTtl, imageTtl - Add applyCacheHeaders function to set Cache-Control based on content type - Apply cache headers to JS, CSS, images, and HTML responses - Add Caching section in UI with TTL configuration inputs Closes #33 --- src/App.tsx | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/code.ts | 57 ++++++++++++++++++++++++++++----- 2 files changed, 141 insertions(+), 7 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 6437c31..d7b4a50 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -44,6 +44,7 @@ import code, { BrandingOptions, SeoOptions, AnalyticsOptions, + CachingOptions, CustomHtmlOptions, Custom404Options, SubdomainRedirect, @@ -162,6 +163,12 @@ export default function App() { const [analytics, setAnalytics] = useState({ googleTagId: "", }); + const [caching, setCaching] = useState({ + enabled: false, + htmlTtl: 60, + staticAssetsTtl: 86400, + imageTtl: 604800, + }); const [customHtml, setCustomHtml] = useState({ headerHtml: "", }); @@ -288,6 +295,17 @@ export default function App() { setCopied(false); } + function handleCachingChange( + field: keyof CachingOptions, + value: boolean | number, + ): void { + setCaching({ + ...caching, + [field]: value, + }); + setCopied(false); + } + function handleCustomHtmlChange( field: keyof CustomHtmlOptions, value: string, @@ -400,6 +418,7 @@ export default function App() { branding, seo, analytics, + caching, customHtml, custom404, subdomainRedirects, @@ -930,6 +949,78 @@ export default function App() { /> + + + + + Cache-Control Headers + + + Improve performance with browser caching + + + + handleCachingChange("enabled", e.target.checked) + } + /> + + + + + handleCachingChange("htmlTtl", Number(e.target.value)) + } + value={caching.htmlTtl} + variant="outlined" + size="small" + /> + + handleCachingChange( + "staticAssetsTtl", + Number(e.target.value), + ) + } + value={caching.staticAssetsTtl} + variant="outlined" + size="small" + /> + + handleCachingChange("imageTtl", Number(e.target.value)) + } + value={caching.imageTtl} + variant="outlined" + size="small" + /> + + + + -1) { const pageId = SLUG_TO_PAGE[url.pathname.slice(1)]; return Response.redirect('https://' + MY_DOMAIN + '/' + pageId, 302); @@ -446,14 +487,14 @@ ${ }); response.headers.delete('Content-Security-Policy'); response.headers.delete('X-Content-Security-Policy'); - return appendJavascript(response, SLUG_TO_PAGE, '404'); + return appendJavascript(response, SLUG_TO_PAGE, '404', url); } // Get current slug from page ID for canonical URL const pageId = url.pathname.slice(-32); const currentSlug = PAGE_TO_SLUG[pageId] || ''; - return appendJavascript(response, SLUG_TO_PAGE, currentSlug); + return appendJavascript(response, SLUG_TO_PAGE, currentSlug, url); } class MetaRewriter { @@ -744,16 +785,18 @@ ${ } } - async function appendJavascript(res, SLUG_TO_PAGE, slug) { + async function appendJavascript(res, SLUG_TO_PAGE, slug, url) { const metaRewriter = new MetaRewriter(slug); const headRewriter = new HeadRewriter(slug); const linkRewriter = new LinkRewriter(); - return new HTMLRewriter() + const contentType = res.headers.get('content-type'); + let transformed = new HTMLRewriter() .on('title', metaRewriter) .on('meta', metaRewriter) .on('link', linkRewriter) .on('head', headRewriter) .on('body', new BodyRewriter(SLUG_TO_PAGE)) .transform(res); + return applyCacheHeaders(transformed, url, contentType); }`; }