From 3e6984e0587a3281d6dff4c09c1281a6eba0141f Mon Sep 17 00:00:00 2001 From: pavlov346346-source Date: Tue, 8 Sep 2026 07:23:18 +0300 Subject: [PATCH] Remove one-time v17.6.7 patch script --- .github/scripts/patch-v1767-classic-offer.mjs | 97 ------------------- 1 file changed, 97 deletions(-) delete mode 100644 .github/scripts/patch-v1767-classic-offer.mjs diff --git a/.github/scripts/patch-v1767-classic-offer.mjs b/.github/scripts/patch-v1767-classic-offer.mjs deleted file mode 100644 index d4abb5f..0000000 --- a/.github/scripts/patch-v1767-classic-offer.mjs +++ /dev/null @@ -1,97 +0,0 @@ -import fs from 'node:fs'; - -const VERSION='17.6.7'; -const RELEASE='20260908-v17-6-7-classic-offer-pdf'; -const read=f=>fs.readFileSync(f,'utf8'); -const write=(f,s)=>fs.writeFileSync(f,s); -function replaceOnce(s,a,b,label){const n=s.split(a).length-1;if(n!==1)throw new Error(`${label}: expected 1, got ${n}`);return s.replace(a,b)} - -// Fix and harden the new classic cover module. -let classic=read('public/core/classic-offer-pdf-v1767.js'); -classic=classic.replace("if(hero)coverImage(ctx,540,170,406,670,0);if(hero)coverImage(ctx,hero,540,170,406,670,0);","if(hero)coverImage(ctx,hero,540,170,406,670,0);"); -classic=classic.replace("const money=v=>Math.round(Number(v||0)).toLocaleString('ru-RU')+' ₽';","const money=v=>Math.round(Number(v||0)).toLocaleString('ru-RU')+' ₽';\n const classicBoxCount=s=>(s?.items||[]).filter(i=>[0,5].includes(Number(i?.categoryId))).reduce((sum,i)=>sum+Math.max(0,Number(i?.qty||0)),0);"); -classic=classic.replaceAll("String(Math.round(Number(s.boxCount||0)))","String(Math.round(classicBoxCount(s)))"); -classic=classic.replaceAll("s.boxCount||0","classicBoxCount(s)||0"); -write('public/core/classic-offer-pdf-v1767.js',classic); - -// Runtime: restore the hidden templates and route every preview/download through the same page renderer. -let runtime=read('public/app-runtime.js'); -const oldIds="const OFFER_TEMPLATE_IDS=new Set(['light','editorial-grid','midnight-glass','emerald-gold']);"; -const newIds="const OFFER_TEMPLATE_IDS=new Set(['light','midnight-glass','editorial-grid','warm-sun','bento-cards','event-story','black-gold','personal-letter','event-ticket','solar-experience','midnight-compact','emerald-gold','neon-emerald']);"; -const idCount=runtime.split(oldIds).length-1;if(idCount<1)throw new Error(`OFFER_TEMPLATE_IDS: expected >=1, got ${idCount}`);runtime=runtime.split(oldIds).join(newIds); -const oldTemplates=` const TEMPLATES=[ - {id:'light',name:'Светлый фирменный',desc:'Классическая A4-композиция: обложка, карточки меню, итог и условия.',thumb:'offer-templates/thumb-light.jpg'}, - {id:'editorial-grid',name:'Солнце Editorial',desc:'Журнальная композиция: крупный первый экран, меню-сетка, фотоблоки и редакционная подача.',thumb:'offer-templates/thumb-editorial-grid.jpg'}, - {id:'midnight-glass',name:'Midnight Glass',desc:'Тёмная презентация: стеклянные панели, итог до меню и контрастная двухколоночная композиция.',thumb:'offer-templates/thumb-midnight-glass.jpg'}, - {id:'emerald-gold',name:'Emerald Gold',desc:'Премиальный буклет: золотой итог на первом экране, условия и меню как финальная часть.',thumb:'offer-templates/thumb-emerald-gold.jpg'} - ];`; -const newTemplates=` const TEMPLATES=[ - {id:'light',name:'Минимализм',desc:'Светлая премиальная классика: крупная обложка, фотография, факты события и чистая сетка.',thumb:'offer-templates/thumb-light.jpg'}, - {id:'midnight-glass',name:'Luxury Dark',desc:'Тёмная ресторанная презентация с золотыми акцентами и крупной food-фотографией.',thumb:'offer-templates/thumb-midnight-glass.jpg'}, - {id:'editorial-grid',name:'Editorial Magazine',desc:'Редакционный журнальный макет с нумерацией разделов, крупной типографикой и фотополосой.',thumb:'offer-templates/thumb-editorial-grid.jpg'}, - {id:'warm-sun',name:'Warm Sun',desc:'Тёплый фирменный дизайн в солнечной палитре с мягкими формами и сценарием подготовки.',thumb:'offer-templates/thumb-light.jpg'}, - {id:'bento-cards',name:'Bento Cards',desc:'Современная карточная композиция: метрики, фото, преимущества и итог собраны в bento-сетку.',thumb:'offer-templates/thumb-light.jpg'}, - {id:'event-story',name:'Event Story',desc:'Предложение как история события: таймлайн от заявки до готового стола и большая фотография.',thumb:'offer-templates/thumb-editorial-grid.jpg'}, - {id:'black-gold',name:'Food First',desc:'Максимальный акцент на еде: полноэкранное фото, чёрный фон и золотая ресторанная подача.',thumb:'offer-templates/thumb-midnight-glass.jpg'}, - {id:'personal-letter',name:'Personal Letter',desc:'Персональное предложение в форме красивого письма клиенту с мягкой типографикой.',thumb:'offer-templates/thumb-light.jpg'}, - {id:'event-ticket',name:'Event Ticket',desc:'Дизайн приглашения/билета: данные события, меню, итог и декоративный ticket-блок.',thumb:'offer-templates/thumb-editorial-grid.jpg'}, - {id:'solar-experience',name:'Solar Experience',desc:'Яркий фирменный солнечный макет с орбитальной композицией и сильным первым экраном.',thumb:'offer-templates/thumb-light.jpg'}, - {id:'midnight-compact',name:'Midnight Compact · архив',desc:'Возвращённый скрытый компактный тёмный шаблон.',thumb:'offer-templates/thumb-midnight-glass.jpg'}, - {id:'emerald-gold',name:'Emerald Gold · архив',desc:'Возвращённый премиальный изумрудно-золотой шаблон.',thumb:'offer-templates/thumb-emerald-gold.jpg'}, - {id:'neon-emerald',name:'Neon Emerald · архив',desc:'Возвращённый скрытый неоново-изумрудный шаблон.',thumb:'offer-templates/thumb-midnight-glass.jpg'} - ];`; -runtime=replaceOnce(runtime,oldTemplates,newTemplates,'template catalog'); -runtime=replaceOnce(runtime,' async function renderOfferPdfPages(s){',' async function renderOfferPdfPagesBase(s){','rename base pdf renderer'); -const insertion=` return canvases;\n }\n async function pdfBlobFromPages(canvases)`; -const wrapper=` return canvases;\n }\n async function renderOfferPdfPages(s){\n const classic=window.SunClassicOfferPDFV1767;\n if(classic?.renderPages)return classic.renderPages(s,renderOfferPdfPagesBase);\n return renderOfferPdfPagesBase(s);\n }\n async function pdfBlobFromPages(canvases)`; -runtime=replaceOnce(runtime,insertion,wrapper,'classic pdf wrapper'); -write('public/app-runtime.js',runtime); - -// Per-order picker must accept the restored IDs. -let ux=read('public/core/ux-fixes-v1764.js'); -ux=replaceOnce(ux," const VERSION='17.6.6';"," const VERSION='17.6.7';",'ux version'); -ux=replaceOnce(ux," const TEMPLATE_IDS=new Set(['light','editorial-grid','midnight-glass','emerald-gold']);"," const TEMPLATE_IDS=new Set(['light','midnight-glass','editorial-grid','warm-sun','bento-cards','event-story','black-gold','personal-letter','event-ticket','solar-experience','midnight-compact','emerald-gold','neon-emerald']);",'ux ids'); -ux=ux.replace('Шаблон закрепляется за этим предложением. Миниатюра повторяет структуру PDF.','Классические и архивные шаблоны. Предварительный просмотр совпадает со скачиваемым PDF.'); -write('public/core/ux-fixes-v1764.js',ux); - -// Performance version only; classic module loads synchronously from index for deterministic PDF preview. -let perf=read('public/core/performance.js'); -perf=replaceOnce(perf," const VERSION='17.6.6';\n const RELEASE='20260908-v17-6-6-offer-templates';",` const VERSION='${VERSION}';\n const RELEASE='${RELEASE}';`,'performance version'); -write('public/core/performance.js',perf); - -// Cache-busting + direct deterministic module load before the app runtime. -let index=read('public/index.html'); -index=index.split('20260908-v17-6-6-offer-templates').join(RELEASE); -const indexNeedle=``; -if(!index.includes(indexNeedle))throw new Error('pdf-engine index script not found'); -index=index.replace(indexNeedle,indexNeedle+``); -write('public/index.html',index); - -let sw=read('public/service-worker.js'); -sw=sw.replace(/const CACHE='sun-catering-pwa-v\d+-[^']+';\nconst VERSION='[^']+';/,`const CACHE='sun-catering-pwa-v72-${RELEASE}';\nconst VERSION='${RELEASE}';`); -sw=replaceOnce(sw,'`./core/sun-safe.js?v=${VERSION}`,`./core/performance.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}`,`./legacy/bootstrap.js?v=${VERSION}`,`./app-runtime.js?v=${VERSION}`,','`./core/sun-safe.js?v=${VERSION}`,`./core/performance.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}`,`./legacy/bootstrap.js?v=${VERSION}`,`./app-runtime.js?v=${VERSION}`,','service worker core'); -write('public/service-worker.js',sw); - -const pkg=JSON.parse(read('package.json'));pkg.version=VERSION;pkg.scripts['check:syntax']=pkg.scripts['check:syntax'].replace('node --check public/core/pdf-engine.js','node --check public/core/pdf-engine.js && node --check public/core/classic-offer-pdf-v1767.js');write('package.json',JSON.stringify(pkg,null,2)+'\n'); -const lock=JSON.parse(read('package-lock.json'));lock.version=VERSION;if(lock.packages?.[''])lock.packages[''].version=VERSION;write('package-lock.json',JSON.stringify(lock,null,2)+'\n'); - -const manifestPath='docs/release-manifest.json',manifest=JSON.parse(read(manifestPath));manifest.version='v17.6.7';manifest.pwaCache=`v72-${RELEASE}`;manifest.release=RELEASE;manifest.clientOfferTemplates=['light','midnight-glass','editorial-grid','warm-sun','bento-cards','event-story','black-gold','personal-letter','event-ticket','solar-experience','midnight-compact','emerald-gold','neon-emerald'];manifest.offerTemplates=13;manifest.classicOfferTemplates=10;manifest.restoredHiddenOfferTemplates=['midnight-compact','emerald-gold','neon-emerald'];manifest.clientOfferPreviewUsesPdfPages=true;manifest.clientOfferAllTemplatesPreviewPdfParity=true;manifest.classicOfferPdfCoverPages=true;manifest.notes='Restored the earlier presentation-style client PDF experience: 10 classic visual concepts plus 3 hidden archive templates. Preview and download are built from the exact same canvas pages.';write(manifestPath,JSON.stringify(manifest,null,2)+'\n'); -write('docs/releases/V17.6.7-CHANGES.txt',`Caterium v17.6.7 — Classic client proposal PDFs\nDate: 2026-09-08\n\n- Restored presentation-style client proposal PDFs based on the earlier beautiful proposal references.\n- 10 classic designs: Минимализм, Luxury Dark, Editorial Magazine, Warm Sun, Bento Cards, Event Story, Food First, Personal Letter, Event Ticket, Solar Experience.\n- Restored hidden archive templates: Midnight Compact, Emerald Gold, Neon Emerald.\n- Every classic template gets a truly different A4 cover composition, not just another palette.\n- Preview and downloaded PDF use the exact same canvas page objects.\n- Existing per-order template selection remains authoritative.\n- PWA cache v72 / ${RELEASE}.\n`); - -// Keep release/security tests current and add v17.6.7 guards. -let staticTest=read('tests/static-security.mjs'); -staticTest=staticTest.replace("const html=readPub('index.html'),legacy=readPub('legacy/bootstrap.js'),runtime=readPub('app-runtime.js'),safe=readPub('core/sun-safe.js'),sw=readPub('service-worker.js'),performance=readPub('core/performance.js'),ops=readPub('core/ops-ux-v1762.js'),hotfix=readPub('core/hotfix-v1763.js'),ux=readPub('core/ux-fixes-v1764.js');","const html=readPub('index.html'),legacy=readPub('legacy/bootstrap.js'),runtime=readPub('app-runtime.js'),safe=readPub('core/sun-safe.js'),sw=readPub('service-worker.js'),performance=readPub('core/performance.js'),ops=readPub('core/ops-ux-v1762.js'),hotfix=readPub('core/hotfix-v1763.js'),ux=readPub('core/ux-fixes-v1764.js'),classic=readPub('core/classic-offer-pdf-v1767.js');"); -staticTest=staticTest.replace("if(!sw.includes('v17-6-6-offer-templates')||!sw.includes('ux-fixes-v1764.js')||sw.includes('offer-gallery-data.js'))fail('service worker cache is stale');else ok('PWA cache updated to v17.6.6');","if(!sw.includes('v17-6-7-classic-offer-pdf')||!sw.includes('classic-offer-pdf-v1767.js')||sw.includes('offer-gallery-data.js'))fail('service worker cache is stale');else ok('PWA cache updated to v17.6.7');"); -staticTest=staticTest.replace("if(html.includes('20260907-v17-6-0-stability-security')||!html.includes('20260908-v17-6-6-offer-templates'))fail('index still serves stale core asset version');else ok('index cache-busting is current');","if(html.includes('20260907-v17-6-0-stability-security')||!html.includes('20260908-v17-6-7-classic-offer-pdf')||!html.includes('classic-offer-pdf-v1767.js'))fail('index still serves stale core asset version');else ok('index cache-busting is current');"); -staticTest=staticTest.replace("if(/service_role\\s*[:=]\\s*['\"][A-Za-z0-9._-]{20,}/i.test(ops+hotfix+ux)||/eyJ[a-zA-Z0-9_-]{30,}/.test(ops+hotfix+ux))fail('possible secret in ops/hotfix/ux module');else ok('ops/hotfix/ux modules have no hard-coded secret');","if(!classic.includes(\"const VERSION='17.6.7'\")||!classic.includes('CLASSIC_IDS')||!classic.includes('ARCHIVE_IDS')||!classic.includes('renderPages'))fail('v17.6.7 classic PDF module missing');else ok('v17.6.7 classic PDF module present');\nif(/service_role\\s*[:=]\\s*['\"][A-Za-z0-9._-]{20,}/i.test(ops+hotfix+ux+classic)||/eyJ[a-zA-Z0-9_-]{30,}/.test(ops+hotfix+ux+classic))fail('possible secret in ops/hotfix/ux/classic module');else ok('ops/hotfix/ux/classic modules have no hard-coded secret');"); -write('tests/static-security.mjs',staticTest); - -let release=read('tests/release-check.mjs'); -release += `\ncheck(pkg.version==='17.6.7','package version is v17.6.7');\ncheck(index.includes('${RELEASE}')&&index.includes('classic-offer-pdf-v1767.js'),'index loads classic proposal PDF module');\ncheck(sw.includes('v72-${RELEASE}')&&sw.includes('classic-offer-pdf-v1767.js'),'service worker caches v17.6.7 classic PDF module');\ncheck(runtime.includes("'warm-sun'")&&runtime.includes("'bento-cards'")&&runtime.includes("'event-story'")&&runtime.includes("'personal-letter'")&&runtime.includes("'event-ticket'")&&runtime.includes("'solar-experience'"),'10 classic proposal designs are available');\ncheck(runtime.includes("'midnight-compact'")&&runtime.includes("'black-gold'")&&runtime.includes("'neon-emerald'"),'hidden archive proposal templates are restored');\ncheck(runtime.includes('SunClassicOfferPDFV1767')&&runtime.includes('renderOfferPdfPagesBase'),'classic renderer wraps the same PDF pages used by preview/download');\n`; -write('tests/release-check.mjs',release); - -let spec=read('tests/app.spec.mjs'); -spec += `\n\ntest('v17.6.7 classic client PDF renders distinctive cover and keeps preview/download page parity', async ({ page }) => {\n await page.goto('/index.html',{waitUntil:'domcontentloaded'});\n const source=fs.readFileSync(path.join(process.cwd(),'public','core','classic-offer-pdf-v1767.js'),'utf8');\n await page.addScriptTag({content:source});\n await page.waitForFunction(()=>Boolean(window.SunClassicOfferPDFV1767),null,{timeout:5000});\n const result=await page.evaluate(async()=>{\n const c=document.createElement('canvas');c.width=1600;c.height=1000;const x=c.getContext('2d');x.fillStyle='#d9a757';x.fillRect(0,0,c.width,c.height);x.fillStyle='#173a2d';x.fillRect(500,120,700,700);const photo=c.toDataURL('image/jpeg',.8);\n const snapshot={id:'1767',client:'Анна',event:'День рождения',date:'2026-09-20',time:'18:30',guests:24,offerTemplateId:'event-ticket',pricing:{base:23517,itemsTotal:23517,total:23517},items:[{id:'a',name:'Фуршетный бокс',qty:3,categoryId:0,photoData:photo,weight:'1500 г'}]};\n const fakeBase=async()=>{const base=document.createElement('canvas');base.width=1600;base.height=2262;return [base]};\n const pages=await window.SunClassicOfferPDFV1767.renderPages(snapshot,fakeBase);return {count:pages.length,id:pages[0].dataset.sunClassicTemplate,cover:pages[0].dataset.sunClassicCover,classic:window.SunClassicOfferPDFV1767.CLASSIC_IDS.length,archive:window.SunClassicOfferPDFV1767.ARCHIVE_IDS.length};\n });\n expect(result.count).toBe(2);expect(result.id).toBe('event-ticket');expect(result.cover).toBe('1');expect(result.classic).toBe(10);expect(result.archive).toBe(3);\n});\n`; -write('tests/app.spec.mjs',spec); - -console.log('Applied Caterium v17.6.7 classic proposal PDF patch');