Caterium v17.6.3 — fix developer gate and SaaS settings
Fix platform developer gate during workspace loading and prevent SaaS click events from reaching Developer Console as DOM buttons. Add v17.6.3 regression tests and PWA cache bump.
This commit is contained in:
parent
5b2926c36b
commit
ce6fa974aa
@ -1,10 +1,10 @@
|
||||
{
|
||||
"name": "caterium-app",
|
||||
"private": true,
|
||||
"version": "17.6.2",
|
||||
"version": "17.6.3",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"check:syntax": "node --check public/app-runtime.js && node --check public/service-worker.js && node --check public/legacy/bootstrap.js && node --check public/core/sun-safe.js && node --check public/core/performance.js && node --check public/core/ops-ux-v1762.js && node --check public/core/pdf-engine.js",
|
||||
"check:syntax": "node --check public/app-runtime.js && node --check public/service-worker.js && node --check public/legacy/bootstrap.js && node --check public/core/sun-safe.js && node --check public/core/performance.js && node --check public/core/hotfix-v1763.js && node --check public/core/ops-ux-v1762.js && node --check public/core/pdf-engine.js",
|
||||
"test:static": "node tests/static-security.mjs",
|
||||
"check:release": "node tests/release-check.mjs",
|
||||
"check:deploy": "npm run check:syntax && npm run test:static && npm run check:release",
|
||||
|
||||
71
public/core/hotfix-v1763.js
Normal file
71
public/core/hotfix-v1763.js
Normal file
@ -0,0 +1,71 @@
|
||||
(()=>{
|
||||
'use strict';
|
||||
if(window.SunHotfixV1763)return;
|
||||
|
||||
const VERSION='17.6.3';
|
||||
const $=id=>document.getElementById(id);
|
||||
const esc=value=>window.SunSafe?.escapeHTML?window.SunSafe.escapeHTML(String(value??'')):String(value??'').replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
|
||||
let checkingGate=false;
|
||||
|
||||
function patchDeveloperOpen(){
|
||||
const dev=window.SunDeveloperV22;
|
||||
if(!dev||dev.__sunV1763SafeOpen||typeof dev.open!=='function')return false;
|
||||
const original=dev.open.bind(dev);
|
||||
dev.open=(source=null)=>original(source instanceof HTMLElement?source:null);
|
||||
Object.defineProperty(dev,'__sunV1763SafeOpen',{value:true,configurable:true});
|
||||
return true;
|
||||
}
|
||||
|
||||
async function platformAdmin(){
|
||||
const dev=window.SunDeveloperV22,cloud=window.SunCloudV2;
|
||||
if(!dev||!cloud?.getSession?.()?.user)return false;
|
||||
if(dev.isPlatformAdmin?.()===true)return true;
|
||||
try{return await dev.checkPlatformAdmin?.(true)===true}catch(_){return false}
|
||||
}
|
||||
|
||||
async function enhanceDeveloperGate(){
|
||||
if(checkingGate)return false;
|
||||
const cloud=window.SunCloudV2,dev=window.SunDeveloperV22;
|
||||
if(!cloud||!dev)return false;
|
||||
const session=cloud.getSession?.(),workspace=cloud.getWorkspace?.();
|
||||
if(!session?.user||workspace)return false;
|
||||
checkingGate=true;
|
||||
try{
|
||||
if(!await platformAdmin())return false;
|
||||
const gate=$('sunCloudAuthGateV3'),card=gate?.querySelector('.sun-cloud-auth-card');
|
||||
if(!gate||!card)return false;
|
||||
if(card.dataset.dev1763==='1')return true;
|
||||
gate.dataset.saasEnhanced='1';
|
||||
card.dataset.devEnhanced='1';
|
||||
card.dataset.dev1763='1';
|
||||
card.innerHTML=`<div class="sun-cloud-auth-brand"><img src="caterium-login-logo.png" alt="Caterium"><div><h2>Аккаунт разработчика</h2><div class="hint">${esc(session.user.email||'')}</div></div></div><p class="hint">Этот аккаунт управляет платформой и не обязан иметь собственную рабочую компанию.</p><div class="sun-cloud-auth-actions"><button class="primary" type="button" data-open-dev-v1763>Открыть кабинет разработчика</button><button class="outline" type="button" data-signout-v1763>Выйти</button></div><div class="sun-cloud-auth-error" id="sunDevGateStatusV1763"></div>`;
|
||||
card.querySelector('[data-open-dev-v1763]')?.addEventListener('click',()=>{patchDeveloperOpen();dev.open?.();});
|
||||
card.querySelector('[data-signout-v1763]')?.addEventListener('click',()=>cloud.signOut?.());
|
||||
return true;
|
||||
}finally{checkingGate=false;}
|
||||
}
|
||||
|
||||
function interceptSaasAdmin(event){
|
||||
const button=event.target?.closest?.('[data-saas-admin]');
|
||||
if(!button)return;
|
||||
const dev=window.SunDeveloperV22;
|
||||
if(!dev)return;
|
||||
event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
patchDeveloperOpen();
|
||||
dev.open?.();
|
||||
}
|
||||
|
||||
function maintain(){
|
||||
patchDeveloperOpen();
|
||||
enhanceDeveloperGate().catch(()=>{});
|
||||
}
|
||||
|
||||
document.addEventListener('click',interceptSaasAdmin,true);
|
||||
window.addEventListener('sun:cloud-permissions-changed',()=>setTimeout(maintain,0));
|
||||
window.addEventListener('sun:cloud-state-applied',()=>setTimeout(maintain,0));
|
||||
setTimeout(maintain,0);
|
||||
setInterval(()=>{if(!document.hidden)maintain();},1000);
|
||||
|
||||
window.SunHotfixV1763={VERSION,patchDeveloperOpen,enhanceDeveloperGate};
|
||||
})();
|
||||
@ -53,7 +53,7 @@
|
||||
const input=e.target;if(!(input instanceof HTMLInputElement)||input.id!=='sunChatFilesV29'||guardedInputs.has(input)||typeof DataTransfer==='undefined')return;
|
||||
e.preventDefault();e.stopImmediatePropagation();guardChatFiles(input).catch(err=>{console.error('[Caterium photo compression]',err);guardedInputs.add(input);input.dispatchEvent(new Event('change',{bubbles:true}));guardedInputs.delete(input);});
|
||||
},true);
|
||||
window.SunAttachmentGuard={VERSION:'17.6.2',MAX_FILE,TARGET,MAX_SIDE,compressImage,prepareAttachment};
|
||||
window.SunAttachmentGuard={VERSION:'17.6.3',MAX_FILE,TARGET,MAX_SIDE,compressImage,prepareAttachment};
|
||||
|
||||
let memoryData=null,memoryAt=0,memoryLoading=false;
|
||||
const esc=v=>window.SunSafe?.escapeHTML?window.SunSafe.escapeHTML(String(v??'')):String(v??'');
|
||||
@ -65,23 +65,27 @@
|
||||
async function enhanceDeveloperMemory(){
|
||||
const view=document.getElementById('sun-developer-console-v22');if(!view?.classList.contains('on'))return;
|
||||
const body=document.getElementById('sunDevBody');if(!body)return;
|
||||
const version=document.getElementById('sunDevReleaseVersion');if(version)version.textContent='17.6.2';
|
||||
const version=document.getElementById('sunDevReleaseVersion');if(version)version.textContent='17.6.3';
|
||||
const d=await loadMemory();if(!d||!view.classList.contains('on'))return;
|
||||
let box=document.getElementById('sunDevMemoryV1761');if(!box){box=document.createElement('div');box.id='sunDevMemoryV1761';box.className='sun-dev-grid';box.style.marginBottom='12px';body.prepend(box);}
|
||||
box.innerHTML=`<div class="sun-dev-kpi"><small>Память сервера</small><b>${esc(d.server_size||d.database_size||'—')}</b></div><div class="sun-dev-kpi"><small>База PostgreSQL</small><b>${esc(d.database_size||'—')}</b></div><div class="sun-dev-kpi"><small>Файлы Storage</small><b>${esc(d.storage_size||'—')}</b></div><div class="sun-dev-kpi"><small>Объектов Storage</small><b>${Number(d.storage_objects||0)}</b></div>`;
|
||||
}
|
||||
function loadHotfix(){
|
||||
if(window.SunHotfixV1763||document.getElementById('sunHotfixV1763Script'))return;
|
||||
const script=document.createElement('script');script.id='sunHotfixV1763Script';script.src='core/hotfix-v1763.js?v=20260907-v17-6-3-developer-hotfix';script.async=true;script.onerror=()=>console.error('[Caterium] Не загрузился модуль hotfix-v1763.js');document.head.appendChild(script);
|
||||
}
|
||||
function loadOpsUX(){
|
||||
if(window.SunOpsUXV1762||document.getElementById('sunOpsUXV1762Script'))return;
|
||||
const script=document.createElement('script');script.id='sunOpsUXV1762Script';script.src='core/ops-ux-v1762.js?v=20260907-v17-6-2-ops-ux';script.async=true;script.onerror=()=>console.error('[Caterium] Не загрузился модуль ops-ux-v1762.js');document.head.appendChild(script);
|
||||
const script=document.createElement('script');script.id='sunOpsUXV1762Script';script.src='core/ops-ux-v1762.js?v=20260907-v17-6-3-developer-hotfix';script.async=true;script.onerror=()=>console.error('[Caterium] Не загрузился модуль ops-ux-v1762.js');document.head.appendChild(script);
|
||||
}
|
||||
const start=()=>{
|
||||
loadOpsUX();scan(document);
|
||||
loadHotfix();loadOpsUX();scan(document);
|
||||
const mo=new MutationObserver(records=>{records.forEach(r=>r.addedNodes.forEach(n=>{if(n.nodeType===1)scan(n)}));enhanceDeveloperMemory();});
|
||||
mo.observe(document.documentElement,{childList:true,subtree:true});
|
||||
document.addEventListener('click',e=>{if(e.target.closest('#sunDeveloperNavV22,[data-dev-tab],#sunDevRefresh'))setTimeout(enhanceDeveloperMemory,100)},true);
|
||||
window.addEventListener('sun:cloud-state-applied',()=>setTimeout(enhanceDeveloperMemory,150));
|
||||
setInterval(()=>{if(document.getElementById('sun-developer-console-v22')?.classList.contains('on'))enhanceDeveloperMemory();},5000);
|
||||
window.SunPerformance={scanImages:()=>scan(document),refreshDeveloperMemory:enhanceDeveloperMemory,loadOpsUX,disconnect:()=>mo.disconnect()};
|
||||
window.SunPerformance={scanImages:()=>scan(document),refreshDeveloperMemory:enhanceDeveloperMemory,loadHotfix,loadOpsUX,disconnect:()=>mo.disconnect()};
|
||||
};
|
||||
if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',start,{once:true});else start();
|
||||
})();
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
const CACHE='sun-catering-pwa-v67-20260907-v17-6-2-ops-ux';
|
||||
const VERSION='20260907-v17-6-2-ops-ux';
|
||||
const CACHE='sun-catering-pwa-v68-20260907-v17-6-3-developer-hotfix';
|
||||
const VERSION='20260907-v17-6-3-developer-hotfix';
|
||||
const CORE=[
|
||||
'./','./index.html',
|
||||
`./core/sun-safe.js?v=${VERSION}`,`./core/performance.js?v=${VERSION}`,`./core/ops-ux-v1762.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/pdf-engine.js?v=${VERSION}`,`./legacy/bootstrap.js?v=${VERSION}`,`./app-runtime.js?v=${VERSION}`,
|
||||
'./offer-gallery/001.jpg','./offer-gallery/002.jpg',
|
||||
'./catalog/001.jpg','./catalog/002.jpg','./catalog/003.jpg',
|
||||
'./sun-logo.png','./caterium-login-logo.png','./pwa-icon-192.png','./pwa-icon-512.png','./manifest.webmanifest',
|
||||
|
||||
@ -65,6 +65,32 @@ test('v17.6.2 operations UX boots with menu and route features', async ({ page }
|
||||
expect(checks.calendarMore).toBeTruthy();
|
||||
expect(await page.locator('header nav button', {hasText:'Меню'}).count()).toBeGreaterThan(0);
|
||||
});
|
||||
test('v17.6.3 developer gate bypasses workspace loading and SaaS click is safe', async ({ page }) => {
|
||||
await page.goto('/index.html', { waitUntil:'domcontentloaded' });
|
||||
await page.evaluate(()=>{
|
||||
document.body.innerHTML='<div id="sunCloudAuthGateV3"><div class="sun-cloud-auth-card"><h2>Загружаю рабочую базу</h2></div></div><button type="button" data-saas-admin>Управление SaaS</button>';
|
||||
window.__devOpenArgs=[];
|
||||
window.SunCloudV2={
|
||||
getSession:()=>({user:{id:'dev',email:'developer@example.com'}}),
|
||||
getWorkspace:()=>null,
|
||||
status:()=>({membershipsLoading:true,membershipsLoaded:false}),
|
||||
signOut:()=>{}
|
||||
};
|
||||
window.SunDeveloperV22={
|
||||
open:arg=>{window.__devOpenArgs.push(arg===null?'null':arg?.constructor?.name||typeof arg)},
|
||||
isPlatformAdmin:()=>true,
|
||||
checkPlatformAdmin:async()=>true
|
||||
};
|
||||
});
|
||||
const content=fs.readFileSync(path.join(process.cwd(),'public','core','hotfix-v1763.js'),'utf8');
|
||||
await page.addScriptTag({content});
|
||||
await page.waitForFunction(()=>document.querySelector('[data-open-dev-v1763]')&&Boolean(window.SunHotfixV1763),null,{timeout:5000});
|
||||
expect(await page.locator('#sunCloudAuthGateV3 h2').textContent()).toBe('Аккаунт разработчика');
|
||||
await page.locator('[data-saas-admin]').click();
|
||||
await page.locator('[data-open-dev-v1763]').click();
|
||||
const args=await page.evaluate(()=>window.__devOpenArgs);
|
||||
expect(args).toEqual(['null','null']);
|
||||
});
|
||||
test('mobile body does not overflow viewport', async ({ page }, testInfo) => {
|
||||
test.skip(testInfo.project.name!=='mobile-390'); await page.goto('/index.html', { waitUntil:'domcontentloaded' }); await page.waitForTimeout(500);
|
||||
const dims=await page.evaluate(()=>({innerWidth,scrollWidth:document.documentElement.scrollWidth,bodyWidth:document.body.scrollWidth}));
|
||||
|
||||
@ -3,16 +3,19 @@ import path from 'node:path';
|
||||
const root=process.cwd(), pub=path.join(root,'public');
|
||||
const read=p=>fs.readFileSync(path.join(pub,p),'utf8');
|
||||
let bad=0;const check=(v,m)=>{console.log(`${v?'OK':'FAIL'}: ${m}`);if(!v)bad++};
|
||||
const index=read('index.html'),runtime=read('app-runtime.js'),sw=read('service-worker.js'),css=read('core/stability-v1760.css'),performance=read('core/performance.js'),ops=read('core/ops-ux-v1762.js');
|
||||
const index=read('index.html'),runtime=read('app-runtime.js'),sw=read('service-worker.js'),css=read('core/stability-v1760.css'),performance=read('core/performance.js'),ops=read('core/ops-ux-v1762.js'),hotfix=read('core/hotfix-v1763.js');
|
||||
check(index.includes('core/stability-v1760.css'),'mobile stability stylesheet loaded');
|
||||
check(css.includes('overflow-x:hidden')&&css.includes('.cats'),'mobile overflow guard present');
|
||||
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('v17-6-2-ops-ux')&&sw.includes('ops-ux-v1762.js'),'service worker cache is v17.6.2');
|
||||
check(sw.includes('v17-6-3-developer-hotfix')&&sw.includes('hotfix-v1763.js'),'service worker cache is v17.6.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('hotfix-v1763.js')&&performance.includes('SunHotfixV1763'),'developer/SaaS hotfix is loaded');
|
||||
check(hotfix.includes('patchDeveloperOpen')&&hotfix.includes('enhanceDeveloperGate')&&hotfix.includes('data-saas-admin'),'developer gate and SaaS click hotfix is versioned');
|
||||
check(hotfix.includes('source instanceof HTMLElement')&&hotfix.includes('stopImmediatePropagation'),'SaaS event object cannot reach Developer Console as a nav button');
|
||||
check(performance.includes('ops-ux-v1762.js'),'operations UX module is loaded by performance core');
|
||||
check(ops.includes('supportReadPermission')&&ops.includes('SUPPORT_POLL_MS=12000'),'developer support read-only live refresh is versioned');
|
||||
check(ops.includes('sun-menu-editor-v1762')&&ops.includes('Премиум боксы'),'full-page menu editor is versioned');
|
||||
@ -20,5 +23,5 @@ check(ops.includes('showCalendarDay')&&ops.includes('.cal-more'),'calendar overf
|
||||
check(ops.includes("ROUTE_BASE_KEY='sunRouteBaseV1'")&&ops.includes('saveRouteStart'),'configurable route origin is versioned');
|
||||
check(ops.includes('showRouteOrder')&&ops.includes('data-route-open'),'route order modal is versioned');
|
||||
check(fs.existsSync(path.join(root,'supabase/functions/caterium-create-employee/index.ts')),'employee Edge Function source is versioned');
|
||||
check(!/sb_secret_[A-Za-z0-9_-]{20,}|service_role\s*[:=]\s*["'][A-Za-z0-9._-]{30,}/i.test(index+runtime+performance+ops),'no client secret-like token');
|
||||
check(!/sb_secret_[A-Za-z0-9_-]{20,}|service_role\s*[:=]\s*["'][A-Za-z0-9._-]{30,}/i.test(index+runtime+performance+ops+hotfix),'no client secret-like token');
|
||||
if(bad)process.exit(1);
|
||||
|
||||
@ -6,7 +6,7 @@ const readPub=p=>fs.readFileSync(path.join(pub,p),'utf8');
|
||||
const readRoot=p=>fs.readFileSync(path.join(root,p),'utf8');
|
||||
const fail=m=>{console.error('FAIL:',m);process.exitCode=1};
|
||||
const ok=m=>console.log('OK:',m);
|
||||
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');
|
||||
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');
|
||||
if(!html.includes('core/sun-safe.js'))fail('SunSafe must load before legacy modules');else ok('shared SunSafe loaded');
|
||||
if(html.includes('offer-gallery-data.js')||fs.existsSync(path.join(pub,'offer-gallery-data.js')))fail('blocking offer-gallery-data.js still present');else ok('base64 gallery removed');
|
||||
for(const raw of ['<span>${b.name}</span>','<span>${o.event}</span>','<span>${o.address||','value="${x[0]}"','value="${x[2]}"']) if(legacy.includes(raw)) fail(`legacy bootstrap contains raw HTML interpolation ${raw}`);
|
||||
@ -27,10 +27,12 @@ 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('v17-6-2-ops-ux')||!sw.includes('ops-ux-v1762.js')||sw.includes('offer-gallery-data.js'))fail('service worker cache is stale');else ok('PWA cache updated');
|
||||
if(!sw.includes('v17-6-3-developer-hotfix')||!sw.includes('hotfix-v1763.js')||sw.includes('offer-gallery-data.js'))fail('service worker cache is stale');else ok('PWA cache updated');
|
||||
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('hotfix-v1763.js')||!performance.includes('SunHotfixV1763'))fail('v17.6.3 hotfix loader missing');else ok('v17.6.3 hotfix loader present');
|
||||
if(!performance.includes('ops-ux-v1762.js')||!performance.includes('SunOpsUXV1762'))fail('ops UX loader missing');else ok('ops UX loader present');
|
||||
for(const marker of ['patchDeveloperOpen','enhanceDeveloperGate','data-saas-admin','stopImmediatePropagation','instanceof HTMLElement']) if(!hotfix.includes(marker))fail(`developer/SaaS hotfix marker missing: ${marker}`);else ok(`developer/SaaS hotfix marker: ${marker}`);
|
||||
for(const marker of ['SUPPORT_POLL_MS=12000','supportReadPermission','sun-menu-editor-v1762','showCalendarDay',"ROUTE_BASE_KEY='sunRouteBaseV1'",'showRouteOrder','routeOpenYandex']) if(!ops.includes(marker)) fail(`ops UX marker missing: ${marker}`); else ok(`ops UX marker: ${marker}`);
|
||||
if(/service_role\s*[:=]\s*['"][A-Za-z0-9._-]{20,}/i.test(ops)||/eyJ[a-zA-Z0-9_-]{30,}/.test(ops))fail('possible secret in ops UX module');else ok('ops UX module has no hard-coded secret');
|
||||
if(/service_role\s*[:=]\s*['"][A-Za-z0-9._-]{20,}/i.test(ops+hotfix)||/eyJ[a-zA-Z0-9_-]{30,}/.test(ops+hotfix))fail('possible secret in ops/hotfix module');else ok('ops/hotfix modules have no hard-coded secret');
|
||||
if(process.exitCode)process.exit(process.exitCode);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user