[object Object]

← back to Hollywood Wallcoverings

Disable CF HTML caching — Cache-Control: no-store

84aed2c0d0f7980abd60595005eeb8733ccd069e · 2026-05-07 14:50:36 -0700 · Steve Abrams

Per fleet sweep: origin must send no-store so CF doesn't cache
edited HTML. /api/* routes unaffected (still cacheable per Express defaults).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit 84aed2c0d0f7980abd60595005eeb8733ccd069e
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu May 7 14:50:36 2026 -0700

    Disable CF HTML caching — Cache-Control: no-store
    
    Per fleet sweep: origin must send no-store so CF doesn't cache
    edited HTML. /api/* routes unaffected (still cacheable per Express defaults).
    
    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---
 server.js | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 65 insertions(+), 6 deletions(-)

diff --git a/server.js b/server.js
index bd9a9fe..93b88a0 100644
--- a/server.js
+++ b/server.js
@@ -26,6 +26,16 @@ const app = express();
 
 // Security headers via helmet (added 2026-05-04 overnight YOLO loop)
 app.use(helmet({ contentSecurityPolicy: false }));
+
+// Disable Cloudflare HTML caching — origin must say no-store so edits propagate
+// immediately. CF respects no-store; max-age=0 alone is still cacheable.
+// (added 2026-05-07 fleet cache-header sweep)
+app.use((req, res, next) => {
+  if (!req.path.startsWith('/api/')) {
+    res.set('Cache-Control', 'no-store, must-revalidate');
+  }
+  next();
+});
 const NORM_TYPE = (t) => {
   if (!t) return 'Other';
   const x = t.replace(/s$/i, '').trim();
@@ -96,9 +106,9 @@ const layout = (title, body, { q='', type='' } = {}) => `<!doctype html>
 <meta charset="utf-8">
 <meta name="viewport" content="width=device-width,initial-scale=1">
 <title>${esc(title)} — Hollywood Wallcoverings</title>
-<meta name="description" content="Hollywood Wallcoverings — commercial-grade Type II vinyls, naturals, and fabrics for interior designers. Free memo samples and trade-only pricing through Designer Wallcoverings.">
+<meta name="description" content="Hollywood Wallcoverings — commercial-grade Type II vinyls, naturals, and fabrics for interior designers. Free memo samples and trade-only pricing.">
 <meta property="og:title" content="${esc(title)} · Hollywood Wallcoverings">
-<meta property="og:description" content="Old-Hollywood glamour for the modern interior. Free memo samples · trade pricing · hand-selected by Designer Wallcoverings.">
+<meta property="og:description" content="Old-Hollywood glamour for the modern interior. Free memo samples · trade pricing · hand-selected for designers.">
 <style>
   :root {
     --bg:#0e0a06;
@@ -146,9 +156,14 @@ const layout = (title, body, { q='', type='' } = {}) => `<!doctype html>
   form.search input{border:1px solid var(--rule);background:var(--bg);padding:9px 14px;border-radius:0;min-width:260px;font:inherit;font-size:13px;outline:none;color:var(--fg);font-family:var(--sans)}
   form.search input:focus{border-color:var(--accent)}
   form.search input::placeholder{color:var(--muted);font-style:italic}
-  form.search button{border:1px solid var(--accent);background:var(--accent);color:var(--bg);padding:9px 18px;font:inherit;font-size:11px;text-transform:uppercase;letter-spacing:.16em;font-weight:600;cursor:pointer;transition:background .15s}
-  form.search button:hover{background:var(--accent-soft)}
+  form.search button{border:1px solid var(--accent);background:transparent;color:var(--muted, var(--accent));padding:9px 18px;font:inherit;font-size:11px;text-transform:uppercase;letter-spacing:.16em;font-weight:600;cursor:pointer;transition:background .15s, color .15s}
+  form.search button:hover{background:var(--accent);color:var(--bg)}
   main{max-width:1400px;margin:0 auto;padding:36px 32px}
+  .controls-bar{display:flex;align-items:center;gap:18px;margin-bottom:18px;flex-wrap:wrap;justify-content:flex-end}
+  .controls-bar label{font-size:10px;letter-spacing:.18em;text-transform:uppercase;color:var(--muted);font-weight:500}
+  .controls-bar select{background:transparent;border:1px solid var(--rule);color:var(--fg);padding:7px 12px;font:inherit;font-size:12px;letter-spacing:.10em;text-transform:uppercase;cursor:pointer;outline:none}
+  .controls-bar select:focus{border-color:var(--accent)}
+  .controls-bar input[type=range]{accent-color:var(--accent);width:140px;cursor:pointer}
   .meta-line{font-size:11px;color:var(--muted);margin-bottom:24px;letter-spacing:.16em;text-transform:uppercase;text-align:center;font-weight:500}
   .meta-line strong{color:var(--accent-soft);font-weight:600}
   .grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:28px}
@@ -261,8 +276,9 @@ document.addEventListener('keydown',e=>{if(e.key==='Escape'&&document.getElement
 </nav>
 ${body}
 <footer>
-  <span class="footer-tag">A private collection by Designer Wallcoverings</span>
-  Free memo samples · Trade pricing · Hand-selected · <a href="${DW_SHOPIFY}">designerwallcoverings.com</a>
+  <span class="footer-tag">A private collection · Old-Hollywood specialists</span>
+  Free memo samples · Trade pricing · Hand-selected
+  <div style="margin-top:10px;font-size:11px;letter-spacing:.14em"><a href="mailto:info@hollywoodwallcoverings.com">info@hollywoodwallcoverings.com</a></div>
   <div style="margin-top:14px;font-size:10px;letter-spacing:.18em">© ${new Date().getFullYear()} Hollywood Wallcoverings · All rights reserved</div>
 </footer>
 </body>
@@ -305,6 +321,19 @@ app.get('/', (req, res) => {
       <div class="meta-line">
         ${q || type ? `Showing <strong>${total.toLocaleString()}</strong> ${type ? `<strong>${esc(type)}</strong>` : ''} ${q ? `matching "<strong>${esc(q)}</strong>"` : ''}` : `<strong>${total.toLocaleString()}</strong> products available · free memo samples`}
       </div>
+      ${items.length === 0 ? '' : `
+      <div class="controls-bar">
+        <label for="sort">Sort</label>
+        <select id="sort">
+          <option value="newest">Newest</option>
+          <option value="color">Color</option>
+          <option value="style">Style</option>
+          <option value="sku">SKU A→Z</option>
+          <option value="title">Title A→Z</option>
+        </select>
+        <label for="density" style="margin-left:14px">Density</label>
+        <input id="density" type="range" min="180" max="380" step="10" value="240">
+      </div>`}
       ${items.length === 0 ? '<div class="empty">No products match. Try a different search or category.</div>' : `<div class="grid" id="grid">${cards}</div>`}
       <div id="loadmore" data-q="${esc(q)}" data-type="${esc(type)}" data-page="${cur}" data-pages="${pages}" style="text-align:center;padding:48px 0;color:var(--muted);font-size:11px;letter-spacing:.18em;text-transform:uppercase">${cur < pages ? 'Loading more…' : '— end —'}</div>
     </main>
@@ -335,6 +364,36 @@ app.get('/', (req, res) => {
       }
       const io=new IntersectionObserver(es=>{es.forEach(e=>{if(e.isIntersecting)loadNext();});},{rootMargin:'400px'});
       io.observe(lm);
+
+      // Sort + density controls (client-side, persisted)
+      const sortSel=document.getElementById('sort');
+      const dens=document.getElementById('density');
+      const STORE='hw_grid';
+      const saved=(()=>{try{return JSON.parse(localStorage.getItem(STORE)||'{}')}catch{return {}}})();
+      function applyDensity(v){grid.style.gridTemplateColumns='repeat(auto-fill,minmax('+v+'px,1fr))';}
+      function colorBucket(t){t=(t||'').toLowerCase();if(/(black|noir|onyx|ink)/.test(t))return 1;if(/(white|ivory|cream|paper|alabaster)/.test(t))return 2;if(/(gold|brass|champagne|bronze|amber|tan|caramel|sand)/.test(t))return 3;if(/(silver|grey|gray|platinum|pewter|smoke)/.test(t))return 4;if(/(red|crimson|burgundy|wine|garnet)/.test(t))return 5;if(/(green|emerald|forest|sage|olive|moss)/.test(t))return 6;if(/(blue|navy|cobalt|sapphire|indigo)/.test(t))return 7;if(/(purple|plum|violet|aubergine|amethyst)/.test(t))return 8;return 9;}
+      function styleBucket(t){t=(t||'').toLowerCase();if(/(damask|toile|baroque|rococo|ornate)/.test(t))return 1;if(/(stripe|trellis|geometric)/.test(t))return 2;if(/(floral|botanical|vine|leaf)/.test(t))return 3;if(/(grass|natural|jute|hemp|raffia|linen|cork)/.test(t))return 4;if(/(metallic|foil|leaf)/.test(t))return 5;return 9;}
+      function sortGrid(mode){
+        const cards=Array.from(grid.querySelectorAll('.card'));
+        const titleOf=c=>(c.querySelector('.t')?.textContent||'').trim();
+        const skuOf=c=>(c.querySelector('.sub')?.textContent||'').trim();
+        if(mode==='color')cards.sort((a,b)=>colorBucket(titleOf(a))-colorBucket(titleOf(b))||titleOf(a).localeCompare(titleOf(b)));
+        else if(mode==='style')cards.sort((a,b)=>styleBucket(titleOf(a))-styleBucket(titleOf(b))||titleOf(a).localeCompare(titleOf(b)));
+        else if(mode==='sku')cards.sort((a,b)=>skuOf(a).localeCompare(skuOf(b)));
+        else if(mode==='title')cards.sort((a,b)=>titleOf(a).localeCompare(titleOf(b)));
+        // newest = original order; clear+re-append in stored order
+        cards.forEach(c=>grid.appendChild(c));
+      }
+      if(sortSel){
+        sortSel.value=saved.sort||'newest';
+        sortGrid(sortSel.value);
+        sortSel.addEventListener('change',()=>{sortGrid(sortSel.value);saved.sort=sortSel.value;localStorage.setItem(STORE,JSON.stringify(saved));});
+      }
+      if(dens){
+        dens.value=saved.density||240;
+        applyDensity(dens.value);
+        dens.addEventListener('input',()=>{applyDensity(dens.value);saved.density=dens.value;localStorage.setItem(STORE,JSON.stringify(saved));});
+      }
     })();
     </script>
   `;

← ffcd697 initial scaffold (gitify-all 2026-05-06)  ·  back to Hollywood Wallcoverings  ·  Add novasuede ns-header to hollywood server-rendered layout 9e6a392 →