From 44d6ec9e818d20034879bb9093e925c3d943507b Mon Sep 17 00:00:00 2001 From: pavlov346346-source Date: Fri, 18 Sep 2026 04:59:16 +0300 Subject: [PATCH] Cache optimized trial photos for legacy URLs in the installed app --- docs/release-manifest.json | 2 +- docs/releases/2026-09-18-SYNC-DEMO-IMAGES.md | 2 +- public/.htaccess | 5 ----- public/core/performance.js | 2 +- public/index.html | 6 +++--- public/service-worker.js | 12 ++++++++++-- tests/release-check.mjs | 10 +++++----- tests/static-security.mjs | 4 ++-- tests/trial-demo.spec.mjs | 10 ++++++++++ 9 files changed, 33 insertions(+), 20 deletions(-) diff --git a/docs/release-manifest.json b/docs/release-manifest.json index e7bdbe7..c8248b5 100644 --- a/docs/release-manifest.json +++ b/docs/release-manifest.json @@ -11,7 +11,7 @@ "serverReady": true, "workspaceAutoDiscovery": true, "invitesTemporarilyDisabled": false, - "pwaCache": "v93-20260918-sync-demo-images", + "pwaCache": "v94-20260918-sync-demo-images-2", "fullOfferDescriptions": true, "dynamicOfferRows": true, "pdfOfferDescriptionFix": true, diff --git a/docs/releases/2026-09-18-SYNC-DEMO-IMAGES.md b/docs/releases/2026-09-18-SYNC-DEMO-IMAGES.md index fbe33a0..f1ab86c 100644 --- a/docs/releases/2026-09-18-SYNC-DEMO-IMAGES.md +++ b/docs/releases/2026-09-18-SYNC-DEMO-IMAGES.md @@ -1,6 +1,6 @@ # Login, synchronization and trial image loading -Release cache: `v93-20260918-sync-demo-images`. +Release cache: `v94-20260918-sync-demo-images-2`. The previous transport aborted every request, including saves, after seven seconds. A slow response could therefore surface as `AbortError: signal is diff --git a/public/.htaccess b/public/.htaccess index 960e128..4d7c95d 100644 --- a/public/.htaccess +++ b/public/.htaccess @@ -17,8 +17,3 @@ ExpiresByType text/html "access plus 0 seconds" ExpiresByType application/javascript "access plus 0 seconds" - - RewriteEngine On - # Compatibility for trial catalogs and saved documents with the original URLs. - RewriteRule ^demo/images/(berry-dessert|bruschetta-tomato|caprese|cheese-fruit|chicken-sandwich|meat-assortment|mushroom-tartlet|salmon-cream|turkey-wrap|vegetables-hummus)\.png$ demo/images/$1.webp [L] - diff --git a/public/core/performance.js b/public/core/performance.js index 82a4c57..75c39a2 100644 --- a/public/core/performance.js +++ b/public/core/performance.js @@ -1,7 +1,7 @@ (()=>{ 'use strict'; const VERSION='17.7.3'; - const RELEASE='20260918-sync-demo-images'; + const RELEASE='20260918-sync-demo-images-2'; const hasStoredSession=()=>{try{return Object.keys(localStorage).some(k=>/^sb-.*-auth-token$/i.test(k)&&String(localStorage.getItem(k)||'').length>20)}catch(_){return false}}; function installAuthBoot(){ diff --git a/public/index.html b/public/index.html index ebe5afe..734980b 100644 --- a/public/index.html +++ b/public/index.html @@ -1,4 +1,4 @@ - - + diff --git a/public/service-worker.js b/public/service-worker.js index 01c18fe..5a6600e 100644 --- a/public/service-worker.js +++ b/public/service-worker.js @@ -1,5 +1,5 @@ -const CACHE='sun-catering-pwa-v93-20260918-sync-demo-images'; -const VERSION='20260918-sync-demo-images'; +const CACHE='sun-catering-pwa-v94-20260918-sync-demo-images-2'; +const VERSION='20260918-sync-demo-images-2'; const CORE=[ './','./index.html',`./core/proposal-layout.js?v=${VERSION}`,'./fonts/Manrope.ttf','./fonts/PlayfairDisplay.ttf','./fonts/PlayfairDisplay-Italic.ttf',`./core/trial-demo.js?v=${VERSION}`,`./core/cloud-transport.js?v=${VERSION}`,`./core/banquet-menu.js?v=${VERSION}`,`./core/access-policy.js?v=${VERSION}`,`./core/import-archive.js?v=${VERSION}`,`./core/company-branding.js?v=${VERSION}`,`./core/signature-offer-pdf-v18.js?v=${VERSION}`,`./core/brand-theme.js?v=${VERSION}`, `./core/sun-safe.js?v=${VERSION}`,`./core/performance.js?v=${VERSION}`,`./core/account-center-v1780.js?v=${VERSION}`,`./core/login-signature-v1776.js?v=${VERSION}`,`./core/data-layer-v1773.js?v=${VERSION}`,`./core/server-automation-v1770.js?v=${VERSION}`,`./core/hotfix-v1763.js?v=${VERSION}`,`./core/ops-ux-v1762.js?v=${VERSION}`,`./core/ux-fixes-v1764.js?v=${VERSION}`,`./core/pdf-engine.js?v=${VERSION}`,`./core/classic-offer-pdf-v1767.js?v=${VERSION}`,`./core/developer-console-v1768.js?v=${VERSION}`,`./core/offer-workspace-v1769.js?v=${VERSION}`,`./core/auth-security-v1774.js?v=${VERSION}`,`./core/order-enhancements-v1775.js?v=${VERSION}`,`./legacy/bootstrap.js?v=${VERSION}`,`./app-runtime.js?v=${VERSION}`, @@ -31,6 +31,14 @@ function forceFresh(req){ self.addEventListener('fetch',event=>{ const req=event.request;if(req.method!=='GET')return; const url=new URL(req.url);if(url.origin!==self.location.origin||url.pathname.startsWith('/api/'))return; + // Static PNGs can bypass Apache rewrites on the production host. Resolve old + // catalog/editor/document URLs here too, before checking the original cache. + if(/^\/demo\/images\/(?:berry-dessert|bruschetta-tomato|caprese|cheese-fruit|chicken-sandwich|meat-assortment|mushroom-tartlet|salmon-cream|turkey-wrap|vegetables-hummus)\.png$/.test(url.pathname)){ + url.pathname=url.pathname.replace(/\.png$/,'.webp'); + const optimized=new Request(url.toString(),{credentials:'same-origin'}); + event.respondWith(caches.match(optimized).then(hit=>hit||fetch(optimized).then(res=>res.ok?cachePut(optimized,res):fetch(req)))); + return; + } if(req.mode==='navigate'){ event.respondWith( fetch(req,{cache:'no-store'}) diff --git a/tests/release-check.mjs b/tests/release-check.mjs index ce90953..341ec4b 100644 --- a/tests/release-check.mjs +++ b/tests/release-check.mjs @@ -14,8 +14,8 @@ check(!index.includes('offer-gallery-data.js'),'blocking Base64 gallery absent') check((runtime.match(/\/Type \/Catalog/g)||[]).length===0,'runtime contains no PDF binary writer'); check(read('core/pdf-engine.js').includes('595.28')&&read('core/pdf-engine.js').includes('841.89'),'PDF engine uses A4 MediaBox'); check([...index.matchAll(/@page\{([^}]*)\}/g)].every(m=>/size:A4/i.test(m[1])),'compact @page rules use A4'); -check(sw.includes('v93-20260918-sync-demo-images')&&sw.includes('data-layer-v1773.js')&&sw.includes('server-automation-v1770.js')&&sw.includes('offer-workspace-v1769.js'),'service worker cache is v17.7.3'); -check(index.includes('20260918-sync-demo-images')&&index.includes('classic-offer-pdf-v1767.js')&&!index.includes('20260907-v17-6-0-stability-security'),'index cache-busting points to v17.7.3'); +check(sw.includes('v94-20260918-sync-demo-images-2')&&sw.includes('data-layer-v1773.js')&&sw.includes('server-automation-v1770.js')&&sw.includes('offer-workspace-v1769.js'),'service worker cache is v17.7.3'); +check(index.includes('20260918-sync-demo-images-2')&&index.includes('classic-offer-pdf-v1767.js')&&!index.includes('20260907-v17-6-0-stability-security'),'index cache-busting points to v17.7.3'); check(performance.includes('SunAttachmentGuard')&&performance.includes('TARGET=2*1024*1024'),'chat photo auto-compression is versioned'); check(performance.includes("rpc('sun_dev_dashboard')")&&performance.includes('server_size')&&performance.includes('storage_size'),'Developer Console server/storage counters are versioned'); check(performance.includes('MEMORY_REFRESH_MS=30000')&&performance.includes('MEMORY_TIMEOUT_MS=8000')&&performance.includes('memoryPromise'),'Developer Console memory refresh is bounded'); @@ -39,7 +39,7 @@ check(!/sb_secret_[A-Za-z0-9_-]{20,}|service_role\s*[:=]\s*["'][A-Za-z0-9._-]{30 check(lock.version===pkg.version&&lock.packages?.['']?.version===pkg.version,'package.json and package-lock.json versions match'); check(releaseManifest.version===`v${pkg.version}`,'release manifest version matches package.json'); check(releaseManifest.channel==='production','release manifest channel is production'); -check(String(releaseManifest.pwaCache||'').includes('v93-20260918-sync-demo-images'),'release manifest points to current PWA cache'); +check(String(releaseManifest.pwaCache||'').includes('v94-20260918-sync-demo-images-2'),'release manifest points to current PWA cache'); check(['17.6.2','17.6.3','17.6.4','17.6.5','17.6.6','17.6.7','17.6.8','17.6.9','17.7.0','17.7.1','17.7.2','17.7.3'].every(v=>fs.existsSync(path.join(root,`docs/releases/V${v}-CHANGES.txt`))),'release notes exist through v17.7.3'); check(runtime.includes('CLOUD_RPC_TIMEOUT_MS=45000')&&runtime.includes('CLOUD_CONFLICT_MAX_RETRIES=4')&&runtime.includes('retryCount'),'cloud sync has timeout and capped exponential conflict retries'); check(runtime.includes("const VERSION = '17.7.3'")&&runtime.includes('ERROR_DEDUPE_MS=5*60*1000')&&runtime.includes('mirrorBusy=false')&&runtime.includes('backupBusy=false'),'stability logger uses current version, dedupe and single-flight guards'); @@ -65,8 +65,8 @@ check(offerWorkspace.includes('PDF и предпросмотр')&&offerWorkspace check(offerWorkspace.includes('SunClassicOfferPDFV1767')&&offerWorkspace.includes('finalGallery=galleryFor'),'custom gallery is injected into PDF renderer'); check(releaseManifest.offerWorkspaceTabs===true&&releaseManifest.offerTemplatesSeparateTab===true&&releaseManifest.offerTwoCustomGalleryPhotos===true,'release manifest records offer workspace changes'); check(pkg.version==='17.7.3','package version is v17.7.3'); -check(index.includes('20260918-sync-demo-images'),'index cache bust is v17.7.3'); -check(sw.includes('v93-20260918-sync-demo-images')&&sw.includes('data-layer-v1773.js')&&sw.includes('server-automation-v1770.js'),'PWA caches v17.7.3 client foundation modules'); +check(index.includes('20260918-sync-demo-images-2'),'index cache bust is v17.7.3'); +check(sw.includes('v94-20260918-sync-demo-images-2')&&sw.includes('data-layer-v1773.js')&&sw.includes('server-automation-v1770.js'),'PWA caches v17.7.3 client foundation modules'); check(fs.existsSync(path.join(root,'public/core/data-layer-v1773.js'))&&fs.existsSync(path.join(root,'public/core/server-automation-v1770.js')),'data layer and server automation modules exist'); check(ux.includes('CateriumServerAutomationV1770?.enabled'),'cloud browser auto completion is disabled when server automation is active'); check(runtime.includes("const VERSION = '17.7.3'")&&runtime.includes("v17.7.3 Clients Server Read"),'stability logger reports v17.7.3'); diff --git a/tests/static-security.mjs b/tests/static-security.mjs index d28d97d..37d971b 100644 --- a/tests/static-security.mjs +++ b/tests/static-security.mjs @@ -28,8 +28,8 @@ if(current!==113)fail(`current catalog photo count ${current}, expected 113`);el if(legacyCount!==60)fail(`legacy catalog photo count ${legacyCount}, expected 60`);else ok('60 legacy catalog photos'); const gallery=fs.readdirSync(path.join(pub,'offer-gallery')).filter(x=>/\.jpg$/i.test(x)); if(gallery.length!==2)fail(`offer gallery contains ${gallery.length} jpg files, expected 2`);else ok('offer gallery trimmed'); -if(!sw.includes('20260918-sync-demo-images')||!sw.includes('login-signature-v1776.js')||!sw.includes('data-layer-v1773.js')||!sw.includes('server-automation-v1770.js')||!sw.includes('offer-workspace-v1769.js')||sw.includes('offer-gallery-data.js'))fail('service worker cache is stale');else ok('PWA cache updated for login refresh'); -if(html.includes('20260907-v17-6-0-stability-security')||html.includes('20260909-v17-7-3-clients-server-read')||!html.includes('20260918-sync-demo-images')||!html.includes('classic-offer-pdf-v1767.js'))fail('index still serves stale core asset version');else ok('index cache-busting is current'); +if(!sw.includes('20260918-sync-demo-images-2')||!sw.includes('login-signature-v1776.js')||!sw.includes('data-layer-v1773.js')||!sw.includes('server-automation-v1770.js')||!sw.includes('offer-workspace-v1769.js')||sw.includes('offer-gallery-data.js'))fail('service worker cache is stale');else ok('PWA cache updated for login refresh'); +if(html.includes('20260907-v17-6-0-stability-security')||html.includes('20260909-v17-7-3-clients-server-read')||!html.includes('20260918-sync-demo-images-2')||!html.includes('classic-offer-pdf-v1767.js'))fail('index still serves stale core asset version');else ok('index cache-busting is current'); if(!performance.includes('SunAttachmentGuard')||!performance.includes('MAX_SIDE=2048'))fail('chat photo compression guard missing');else ok('chat photo compression guard present'); if(!performance.includes("rpc('sun_dev_dashboard')")||!performance.includes('storage_size')||!performance.includes('server_size'))fail('Developer Console memory counters missing');else ok('Developer Console memory counters present'); if(performance.includes('records.forEach(r=>r.addedNodes.forEach(n=>{if(n.nodeType===1)scan(n)}));enhanceDeveloperMemory()'))fail('Developer Console memory refresh is still coupled to MutationObserver');else ok('Developer Console memory refresh loop removed'); diff --git a/tests/trial-demo.spec.mjs b/tests/trial-demo.spec.mjs index 0ac6aef..3b5aa33 100644 --- a/tests/trial-demo.spec.mjs +++ b/tests/trial-demo.spec.mjs @@ -2,6 +2,16 @@ import {test,expect} from '@playwright/test'; import fs from 'node:fs'; import {demo} from '../ops/demo/trial-demo-data.mjs'; +test('installed app serves old trial PNG links as cached lightweight images even offline',async({page,context})=>{ + await page.route('**://api.caterium.ru/**',r=>r.abort());await page.route('**://*.supabase.co/**',r=>r.abort()); + await page.goto('/index.html',{waitUntil:'domcontentloaded'}); + await page.waitForFunction(()=>Boolean(navigator.serviceWorker.controller)); + const load=()=>page.evaluate(async d=>Promise.all(d.boxes.map(async b=>{const r=await fetch('/'+b.photo);return {type:r.headers.get('content-type'),bytes:(await r.arrayBuffer()).byteLength}})),demo); + const online=await load();expect(online.every(r=>r.type.includes('webp'))).toBe(true);expect(online.reduce((s,r)=>s+r.bytes,0)).toBe(1343650); + await expect.poll(()=>page.evaluate(async d=>(await Promise.all(d.boxes.map(b=>caches.match('/'+b.photo.replace('.png','.webp'))))).filter(Boolean).length,demo)).toBe(10); + await context.setOffline(true);expect(await load()).toEqual(online);await context.setOffline(false); +}); + async function ready(page){ page.on('dialog',d=>d.accept()); await page.route('**://api.caterium.ru/**',r=>r.abort());await page.route('**://*.supabase.co/**',r=>r.abort());