From 4fe984b584162ca47934295dabdec8f409d2ce8a Mon Sep 17 00:00:00 2001 From: pavlov346346-source Date: Fri, 18 Sep 2026 05:48:20 +0300 Subject: [PATCH] fix: remove legacy login flash during session restoration --- docs/release-manifest.json | 5 ++- docs/releases/2026-09-18-SINGLE-LOGIN.md | 9 +++++ public/app-runtime.js | 35 +++++++++++++------- public/core/login-signature-v1776.css | 20 +++++++++++ public/core/login-signature-v1776.js | 22 ++----------- public/core/performance.js | 15 ++++++--- public/index.html | 13 +++----- public/service-worker.js | 8 ++--- tests/login-recovery.spec.mjs | 42 ++++++++++++++++++++++++ tests/release-check.mjs | 10 +++--- tests/static-security.mjs | 4 +-- 11 files changed, 126 insertions(+), 57 deletions(-) create mode 100644 docs/releases/2026-09-18-SINGLE-LOGIN.md create mode 100644 public/core/login-signature-v1776.css diff --git a/docs/release-manifest.json b/docs/release-manifest.json index c9695b9..8e43e17 100644 --- a/docs/release-manifest.json +++ b/docs/release-manifest.json @@ -11,7 +11,7 @@ "serverReady": true, "workspaceAutoDiscovery": true, "invitesTemporarilyDisabled": false, - "pwaCache": "v95-20260918-proposal-loading", + "pwaCache": "v96-20260918-single-login", "fullOfferDescriptions": true, "dynamicOfferRows": true, "pdfOfferDescriptionFix": true, @@ -393,6 +393,9 @@ "supabaseProjectRef": "usfjwhztqoopzzfmfbis", "recoveryMigration": "20260917150000_fresh_caterium", "anonymousAccessDisabled": true, + "singleLoginFirstPaint": true, + "loginWaitsForSessionRestore": true, + "legacyLocalLoginRemoved": true, "clientCacheTenantIsolation": true, "trialDemo": { "version": 1, diff --git a/docs/releases/2026-09-18-SINGLE-LOGIN.md b/docs/releases/2026-09-18-SINGLE-LOGIN.md new file mode 100644 index 0000000..b581b14 --- /dev/null +++ b/docs/releases/2026-09-18-SINGLE-LOGIN.md @@ -0,0 +1,9 @@ +# One login screen from the first frame + +Release cache: `v96-20260918-single-login`. + +The older blue/white email/password card could appear before the asynchronously loaded login decoration script. A saved session also initially looked signed out while the SDK restored it. The current cream login stylesheet is now loaded directly by the document and cached with the application. Its title, company mark and fields are created in their final form, independent of optional decoration JavaScript. The obsolete card styles and old local user/PIN popup have been removed. + +An explicit session-restoration state keeps email/password fields absent until the SDK confirms that login is necessary. Startup shows a neutral Caterium loading message instead of imitation input fields. Startup cover cleanup works even if the decorative script fails. SDK loading and session restoration have deadlines, and authentication remains required; the emergency local switch remains disabled. + +Regression coverage delays session restoration, blocks the decorative script, seeds old local-role preferences, switches login/registration modes, and verifies the authenticated app opens without ever mounting a password form. Existing account separation, real SDK login and retry checks remain enabled. diff --git a/public/app-runtime.js b/public/app-runtime.js index f0edc09..171b98e 100644 --- a/public/app-runtime.js +++ b/public/app-runtime.js @@ -2181,6 +2181,7 @@ window.SUN_LEGACY_CATALOG_V175=[]; let client = null; let clientSignature = ''; let session = null; + let authLoading = true; let memberships = []; let workspace = null; let supportMode = null; @@ -3181,13 +3182,14 @@ window.SUN_LEGACY_CATALOG_V175=[]; const existing=$('sunSupabaseJsV2'); if(existing){for(let i=0;i<80&&!window.supabase?.createClient;i++)await sleep(100);if(window.supabase?.createClient)return;} await new Promise((resolve,reject)=>{ - const script=document.createElement('script');script.id='sunSupabaseJsV2';script.src=SUPABASE_JS;script.async=true;script.onload=resolve;script.onerror=()=>reject(new Error('Не удалось загрузить библиотеку Supabase. Проверьте интернет.'));document.head.appendChild(script); + const script=document.createElement('script'),timer=setTimeout(()=>{script.remove();reject(new Error('Не удалось загрузить сервис входа. Обновите страницу и проверьте интернет.'))},12000);script.id='sunSupabaseJsV2';script.src=SUPABASE_JS;script.async=true;script.onload=()=>{clearTimeout(timer);resolve()};script.onerror=()=>{clearTimeout(timer);script.remove();reject(new Error('Не удалось загрузить библиотеку Supabase. Проверьте интернет.'))};document.head.appendChild(script); }); if(!window.supabase?.createClient)throw new Error('Supabase SDK не загрузился.'); } async function connectFromConfig(silent=true) { - if(!config.url||!config.key){setStatus('local','Облако не подключено.');return;} + if(!config.url||!config.key){finishAuthRestore();setStatus('local','Облако не подключено.');return;} + authLoading=true; setStatus('syncing','Подключаю Supabase…'); try{ await loadSupabaseLibrary(); @@ -3196,7 +3198,7 @@ window.SUN_LEGACY_CATALOG_V175=[]; client=window.supabase.createClient(config.url,config.key,{auth:{persistSession:true,autoRefreshToken:true,detectSessionInUrl:true},global:{fetch:supabaseProxyFetch}});clientSignature=signature; client.auth.onAuthStateChange((_event,next)=>{const changed=session?.user?.id!==next?.user?.id;if(changed)window.CateriumBranding?.resetSidebar();session=next;if(!changed&&(membershipsLoading||(membershipsLoaded&&!membershipError)))return;membershipsLoading=Boolean(next?.user);membershipsLoaded=!next?.user;setTimeout(async()=>{try{if(!next?.user){const sw=await switchTenantLocal('');if(sw.changed){location.reload();return;}}else{const sw=await loadMemberships();if(sw){location.reload();return;}}}finally{renderCloudUI();updatePill();}},0);}); } - const {data,error}=await client.auth.getSession();if(error)throw error;session=data.session||null; + const {data,error}=await sunCloudAwait(client.auth.getSession(),'Проверка входа');if(error)throw error;session=data.session||null;finishAuthRestore(); const tenantSwitched=session?await loadMemberships():(await switchTenantLocal('')).changed; if(tenantSwitched){location.reload();return;} if(session&&workspace){ @@ -3213,7 +3215,12 @@ window.SUN_LEGACY_CATALOG_V175=[]; else setStatus('auth','Подключено. Войдите в аккаунт.'); if(!silent)toast('Supabase подключён.','success'); }catch(error){handleError(error,'Не удалось подключиться к Supabase.');} - finally{renderCloudUI();} + finally{finishAuthRestore();renderCloudUI();} + } + + function finishAuthRestore(){ + if(!authLoading)return;authLoading=false; + window.dispatchEvent(new CustomEvent('sun:cloud-permissions-changed')); } async function signIn(signUp) { @@ -3468,12 +3475,12 @@ window.SUN_LEGACY_CATALOG_V175=[]; setAutoSyncDeveloper, snapshotLocal:()=>collectLocalPayload(), reloadMemberships:async()=>{await loadMemberships();renderCloudUI();return workspace?clone(workspace):null;}, - status:()=>({connected:Boolean(client),signedIn:Boolean(session),supportMode:supportMode?clone(supportMode):null,workspace:workspace?clone(workspace):null,membershipsLoading,membershipsLoaded,membershipError,membershipCount:memberships.length,dirty,lastStatus,lastError}) + status:()=>({connected:Boolean(client),signedIn:Boolean(session),authLoading,supportMode:supportMode?clone(supportMode):null,workspace:workspace?clone(workspace):null,membershipsLoading,membershipsLoaded,membershipError,membershipCount:memberships.length,dirty,lastStatus,lastError}) }; async function boot(){ injectStyles();disableLegacyCloud();installStorageTracking();hookEnterpriseSchedule();observeSettings();updatePill(); - if(config.url&&config.key)await connectFromConfig(true);else setStatus('local','Облако не подключено. Откройте Настройки → Облако Supabase.'); + if(config.url&&config.key)await connectFromConfig(true);else{finishAuthRestore();setStatus('local','Облако не подключено. Откройте Настройки → Облако Supabase.');} } if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',boot,{once:true});else boot(); @@ -3564,7 +3571,7 @@ window.SUN_LEGACY_CATALOG_V175=[]; .sun-rbac-toolbar{display:flex;gap:8px;align-items:center;justify-content:space-between;flex-wrap:wrap;margin:8px 0 12px} .sun-rbac-members{display:grid;gap:8px}.sun-rbac-member{display:grid;grid-template-columns:minmax(220px,1.4fr) minmax(150px,.7fr) auto;gap:10px;align-items:center;border:1px solid #dfe5e8;border-radius:10px;padding:10px 12px;background:#fff} .sun-rbac-member small{display:block;color:#75808a;margin-top:3px}.sun-rbac-off{opacity:.58}.sun-rbac-perm-groups{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;margin-top:12px}.sun-rbac-group{border:1px solid #dfe5e8;border-radius:10px;padding:10px;background:#fafcfc}.sun-rbac-group h4{margin:0 0 8px;color:#17384d}.sun-rbac-check{display:flex!important;flex-direction:row!important;align-items:center!important;gap:8px!important;margin:6px 0!important}.sun-rbac-check input{width:17px!important;height:17px!important;flex:0 0 auto}.sun-rbac-role-line{display:grid;grid-template-columns:1.2fr 1fr auto;gap:10px;align-items:end}.sun-rbac-danger{margin-left:auto}.sun-rbac-section-title{display:flex;align-items:center;justify-content:space-between;gap:10px;margin:18px 0 8px}.sun-rbac-section-title h3{margin:0;color:#17384d;font-size:15px}.sun-rbac-invite{display:grid;grid-template-columns:minmax(190px,1.2fr) minmax(130px,.65fr) auto;gap:10px;align-items:center;border:1px dashed #cfd9df;border-radius:10px;padding:10px 12px;background:#fbfcfc}.sun-rbac-invite small{display:block;color:#75808a;margin-top:3px}.sun-rbac-invite-form{border:1px solid #dfe5e8;border-radius:12px;padding:14px;background:#fbfcfc}.sun-rbac-invite-grid{display:grid;grid-template-columns:1.2fr 1.5fr 1fr;gap:10px}.sun-rbac-linkbox{margin-top:12px;padding:10px;border-radius:9px;background:#f1f5f6;word-break:break-all;font:600 12px/1.45 ui-monospace,monospace}.sun-rbac-lock-note{font-size:12px;color:#6e7880;margin-top:6px}.sun-cloud-identity-v3{font-size:12px;color:#adc0cc;padding:3px 7px}.sun-cloud-pin-card{border-top:1px solid #e2e7e9;margin-top:10px;padding-top:10px} - body.sun-cloud-auth-required .view,body.sun-cloud-auth-required header{filter:blur(2px);pointer-events:none;user-select:none}.sun-cloud-auth-gate{position:fixed;inset:0;z-index:10000;background:#132f44eb;display:grid;place-items:center;padding:20px}.sun-cloud-auth-card{width:min(430px,100%);background:#fff;border-radius:16px;padding:22px;box-shadow:0 24px 80px #0005}.sun-cloud-auth-brand{display:flex;align-items:center;gap:12px;margin-bottom:16px}.sun-cloud-auth-brand img{width:58px;height:58px;object-fit:contain}.sun-cloud-auth-card h2{margin:0;color:#17384d}.sun-cloud-auth-card label{display:block;margin:10px 0}.sun-cloud-auth-card input{width:100%}.sun-auth-password{position:relative}.sun-auth-password input{padding-right:46px}.sun-auth-eye{position:absolute;right:7px;bottom:6px;width:34px;height:34px;border:0;background:transparent;border-radius:8px;cursor:pointer;font-size:17px;color:#536674}.sun-auth-eye:hover{background:#eef2f3}.sun-auth-switch{margin:14px 0 0;text-align:center;font-size:13px}.sun-auth-switch button{border:0;background:transparent;color:#245c7c;font-weight:800;cursor:pointer;text-decoration:underline;text-underline-offset:3px}.sun-cloud-auth-actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:12px}.sun-cloud-auth-error{color:#9a3838;font-size:12px;margin-top:8px;min-height:18px}.sun-auth-mode-tabs{display:grid;grid-template-columns:1fr 1fr;gap:6px;background:#f2f4f5;border-radius:11px;padding:4px;margin:10px 0 14px}.sun-auth-mode-tabs button{border:0;border-radius:8px;padding:9px 10px;background:transparent;font-weight:800;cursor:pointer}.sun-auth-mode-tabs button.on{background:#fff;box-shadow:0 2px 10px #0001;color:#17384d}.sun-auth-trial{background:#eef8f2;color:#286447;border-radius:10px;padding:10px 11px;font-size:12px;font-weight:700;margin:10px 0}.sun-auth-success{background:#eef8f2;color:#286447;border-radius:10px;padding:10px 11px;font-size:12px;margin-top:10px} + body.sun-cloud-auth-required .view,body.sun-cloud-auth-required header{filter:blur(2px);pointer-events:none;user-select:none}.sun-auth-password{position:relative}.sun-auth-password input{padding-right:46px}.sun-auth-eye{position:absolute;right:7px;bottom:6px;width:34px;height:34px;border:0;background:transparent;border-radius:8px;cursor:pointer;font-size:17px;color:#536674}.sun-auth-eye:hover{background:#eef2f3}.sun-auth-switch{margin:14px 0 0;text-align:center;font-size:13px}.sun-auth-switch button{border:0;background:transparent;color:#245c7c;font-weight:800;cursor:pointer;text-decoration:underline;text-underline-offset:3px}.sun-cloud-auth-actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:12px}.sun-cloud-auth-error{color:#9a3838;font-size:12px;margin-top:8px;min-height:18px}.sun-auth-mode-tabs{display:grid;grid-template-columns:1fr 1fr;gap:6px;background:#f2f4f5;border-radius:11px;padding:4px;margin:10px 0 14px}.sun-auth-mode-tabs button{border:0;border-radius:8px;padding:9px 10px;background:transparent;font-weight:800;cursor:pointer}.sun-auth-mode-tabs button.on{background:#fff;box-shadow:0 2px 10px #0001;color:#17384d}.sun-auth-trial{background:#eef8f2;color:#286447;border-radius:10px;padding:10px 11px;font-size:12px;font-weight:700;margin:10px 0}.sun-auth-success{background:#eef8f2;color:#286447;border-radius:10px;padding:10px 11px;font-size:12px;margin-top:10px} @media(max-width:760px){.sun-rbac-perm-groups{grid-template-columns:1fr}.sun-rbac-member,.sun-rbac-invite{grid-template-columns:1fr}.sun-rbac-role-line,.sun-rbac-invite-grid{grid-template-columns:1fr}} `;document.head.appendChild(style); } @@ -3829,7 +3836,7 @@ window.SUN_LEGACY_CATALOG_V175=[]; function currentInviteToken(){try{return String(new URL(location.href).searchParams.get(INVITE_QUERY_KEY)||'').trim()}catch(_){return ''}} function clearInviteFromUrl(){try{const u=new URL(location.href);u.searchParams.delete(INVITE_QUERY_KEY);history.replaceState(null,'',u.toString())}catch(_){}} function passwordField(id,label,autocomplete='current-password'){ - return ``; + return ``; } function bindPasswordEyes(root){qa('[data-toggle-password]',root).forEach(b=>b.onclick=()=>{const i=root.querySelector(`#${b.dataset.togglePassword}`);if(!i)return;i.type=i.type==='password'?'text':'password';b.textContent=i.type==='password'?'◉':'×';b.setAttribute('aria-label',i.type==='password'?'Показать пароль':'Скрыть пароль');});} async function getInvitePreview(token){ @@ -3869,11 +3876,15 @@ window.SUN_LEGACY_CATALOG_V175=[]; } if(st.signedIn&&ws){$('sunCloudAuthGateV3')?.remove();document.body.classList.remove('sun-cloud-auth-required');try{localStorage.removeItem(LOCAL_ONLY_KEY)}catch(_){}return;} if(!st.signedIn&&window.CateriumAccessPolicy?.emergencyLocalActive()){$('sunCloudAuthGateV3')?.remove();document.body.classList.remove('sun-cloud-auth-required');return;} - const gateState=st.signedIn?(st.membershipsLoading?'loading':st.membershipError?'error':'missing'):'login'; + const gateState=st.signedIn?(st.membershipsLoading?'loading':st.membershipError?'error':'missing'):st.authLoading?'restoring':'login'; const currentGate=$('sunCloudAuthGateV3'); - if(currentGate){if(st.signedIn&¤tGate.dataset.authState!==gateState)currentGate.remove();else{if(st.signedIn){const e=$('sunGateErrorV3');if(e)e.textContent=st.membershipsLoading?'Загружаю рабочую базу…':st.membershipError||e.textContent;}return;}} + if(currentGate){if(currentGate.dataset.authState!==gateState)currentGate.remove();else{if(st.signedIn){const e=$('sunGateErrorV3');if(e)e.textContent=st.membershipsLoading?'Загружаю рабочую базу…':st.membershipError||e.textContent;}return;}} document.body.classList.add('sun-cloud-auth-required'); const gate=document.createElement('div');gate.id='sunCloudAuthGateV3';gate.className='sun-cloud-auth-gate';gate.dataset.authState=gateState; + if(gateState==='restoring'){ + gate.innerHTML='
Caterium

Открываю Caterium…

Проверяю сохранённый вход

'; + document.body.appendChild(gate);return; + } if(st.signedIn&&st.membershipError){ gate.innerHTML=`

Не удалось загрузить рабочую базу

${esc(ss?.user?.email||'Аккаунт авторизован')}

Вход выполнен, но сервер не вернул данные компании. Повторите загрузку.

${esc(st.membershipError)}
`; document.body.appendChild(gate); @@ -3887,12 +3898,12 @@ window.SUN_LEGACY_CATALOG_V175=[]; gate.querySelector('#sunGateRetryWorkspaceV3').onclick=async()=>{const e=gate.querySelector('#sunGateErrorV3');if(getPendingRegistration(ss.user.email||''))return finishPendingRegistration(gate,ss.user.email||'');if(!st.membershipsLoading){const companyName=String(gate.querySelector('#sunGateRecoveryCompanyV25')?.value||'').trim();if(!companyName){e.textContent='Введите название компании.';return;}savePendingRegistration(ss.user.email||'',companyName);return finishPendingRegistration(gate,ss.user.email||'');}e.textContent='Проверяю доступ…';try{const found=await cloud()?.reloadMemberships?.();if(found){gate.remove();document.body.classList.remove('sun-cloud-auth-required');await cloud()?.pull?.();setTimeout(applyNavPermissions,100);}else e.textContent='Рабочая база для этого аккаунта не найдена.';}catch(err){e.textContent=errText(err,'Не удалось проверить доступ.');}}; gate.querySelector('#sunGateSignOutV3').onclick=async()=>{if(window.SunCloudV2?.signOut)return window.SunCloudV2.signOut();try{await client()?.auth.signOut();}catch(_){}location.reload();};return; } - gate.innerHTML=`
Caterium

Вход в Caterium

Введите данные своего аккаунта
${passwordField('sunGatePasswordV3','Пароль','current-password')}

Нет аккаунта?

`;document.body.appendChild(gate);bindPasswordEyes(gate); + gate.innerHTML=`
Caterium

Войти в рабочее пространство

Ваши заказы. Ваша команда. Ваш результат.
${passwordField('sunGatePasswordV3','Пароль','current-password')}

Нет аккаунта?

`;document.body.appendChild(gate);bindPasswordEyes(gate); const localOnlyButton=gate.querySelector('#sunGateLocalOnlyV1'); if(window.CateriumAccessPolicy?.emergencyLocalEnabled){localOnlyButton.onclick=()=>{try{localStorage.setItem(LOCAL_ONLY_KEY,'1')}catch(_){}location.reload();};} else localOnlyButton.closest('p').remove(); const emailInput=gate.querySelector('#sunGateEmailV3'),passwordInput=gate.querySelector('#sunGatePasswordV3'),password2Input=gate.querySelector('#sunGatePassword2V27'),companyInput=gate.querySelector('#sunGateCompanyV3'),submitBtn=gate.querySelector('#sunGateSubmitV3'),gateError=gate.querySelector('#sunGateErrorV3');let registerMode=false; - const setMode=(registration)=>{registerMode=Boolean(registration);gate.querySelector('#sunGateRegisterFieldsV27').hidden=!registerMode;gate.querySelector('#sunGateConfirmWrapV27').hidden=!registerMode;gate.querySelector('#sunGateTitleV3').textContent=registerMode?'Создать аккаунт Caterium':'Вход в Caterium';gate.querySelector('#sunGateSubtitleV3').textContent=registerMode?'Заполните данные — после регистрации Caterium откроется автоматически':'Введите данные своего аккаунта';submitBtn.textContent=registerMode?'Создать аккаунт':'Войти';gate.querySelector('#sunGateSwitchTextV27').textContent=registerMode?'Уже есть аккаунт?':'Нет аккаунта?';gate.querySelector('#sunGateSwitchV27').textContent=registerMode?'Войти':'Создать аккаунт';passwordInput.autocomplete=registerMode?'new-password':'current-password';gateError.textContent='';if(registerMode)setTimeout(()=>companyInput.focus(),30);else setTimeout(()=>emailInput.focus(),30)}; + const setMode=(registration)=>{registerMode=Boolean(registration);gate.querySelector('#sunGateRegisterFieldsV27').hidden=!registerMode;gate.querySelector('#sunGateConfirmWrapV27').hidden=!registerMode;gate.querySelector('#sunGateTitleV3').textContent=registerMode?'Создать аккаунт Caterium':'Войти в рабочее пространство';gate.querySelector('#sunGateSubtitleV3').textContent=registerMode?'Заполните данные — после регистрации Caterium откроется автоматически':'Ваши заказы. Ваша команда. Ваш результат.';submitBtn.textContent=registerMode?'Создать аккаунт':'Войти';gate.querySelector('#sunGateSwitchTextV27').textContent=registerMode?'Уже есть аккаунт?':'Нет аккаунта?';gate.querySelector('#sunGateSwitchV27').textContent=registerMode?'Войти':'Создать аккаунт';passwordInput.autocomplete=registerMode?'new-password':'current-password';gateError.textContent='';if(registerMode)setTimeout(()=>companyInput.focus(),30);else setTimeout(()=>emailInput.focus(),30)}; async function auth(){const c=client(),email=emailInput.value.trim(),password=passwordInput.value,password2=password2Input.value,companyName=String(companyInput.value||'').trim();if(!c){gateError.textContent='Облачный сервис не подключён.';return;}if(registerMode&&!companyName){gateError.textContent='Введите название компании.';return;}if(!email||password.length<6){gateError.textContent='Введите email и пароль минимум из 6 символов.';return;}if(registerMode&&password!==password2){gateError.textContent='Пароли не совпадают.';return;}submitBtn.disabled=true;gateError.textContent=registerMode?'Создаю аккаунт…':'Выполняю вход…';try{let r;if(registerMode){savePendingRegistration(email,companyName);const existing=await c.auth.signInWithPassword({email,password});if(!existing.error&&existing.data.session){r=existing;}else{r=await c.auth.signUp({email,password,options:{data:{company_name:companyName,registration_source:'caterium_public_signup'}}});if(r.error)throw r.error;if(!r.data.session){const retry=await c.auth.signInWithPassword({email,password});if(retry.error){clearPendingRegistration();throw new Error('Этот email уже зарегистрирован. Войдите в аккаунт или восстановите пароль.');}r=retry;}}}else r=await c.auth.signInWithPassword({email,password});if(r.error)throw r.error;const authUser=r.data.user||r.data.session?.user||null;const userId=authUser?.id||'';if(userId)sessionStorage.setItem(`sunCloudQuickPinUnlockedV3:${userId}`,'1');gateError.textContent=registerMode?'Аккаунт готов. Создаю компанию…':'Вход выполнен. Загружаю рабочую базу…';setTimeout(async()=>{await cloud()?.reloadMemberships?.();if(registerMode&&workspace()){clearPendingRegistration();gate.remove();document.body.classList.remove('sun-cloud-auth-required');await cloud()?.pull?.();applyNavPermissions();return;}if(getPendingRegistration(email)&&!workspace()){await finishPendingRegistration(gate,email);return;}if(workspace()){gate.remove();document.body.classList.remove('sun-cloud-auth-required');await cloud()?.pull?.();applyNavPermissions();}else{gate.remove();ensureAuthGate();}},180);}catch(err){gateError.textContent=errText(err,'Не удалось выполнить операцию.')}finally{submitBtn.disabled=false}} gate.querySelector('#sunGateSwitchV27').onclick=()=>setMode(!registerMode);submitBtn.onclick=auth;passwordInput.addEventListener('keydown',e=>{if(e.key==='Enter'&&!registerMode)auth()});password2Input.addEventListener('keydown',e=>{if(e.key==='Enter'&®isterMode)auth()});setMode(false); } diff --git a/public/core/login-signature-v1776.css b/public/core/login-signature-v1776.css new file mode 100644 index 0000000..8863a8b --- /dev/null +++ b/public/core/login-signature-v1776.css @@ -0,0 +1,20 @@ + +body.sun-cloud-auth-required{overflow:hidden!important;background:#f5f0e7!important}body.sun-cloud-auth-required>header,body.sun-cloud-auth-required>.view{visibility:hidden!important} +#sunCloudAuthGateV3.sun-cloud-auth-gate{position:fixed!important;inset:0!important;z-index:20000!important;overflow:auto!important;display:grid!important;place-items:center!important;padding:clamp(32px,6vh,78px) clamp(24px,8vw,120px)!important;background:radial-gradient(circle at 18% 18%,rgba(255,255,255,.92),transparent 34%),radial-gradient(circle at 77% 22%,rgba(255,255,255,.5),transparent 28%),linear-gradient(135deg,#f8f4ec 0%,#f2ece1 56%,#f7f3eb 100%)!important;color:#282621!important} +#sunCloudAuthGateV3.sun-cloud-auth-gate:before{content:'C';position:fixed;z-index:0;left:-7vw;top:49%;transform:translateY(-50%);pointer-events:none;font:400 min(72vw,860px)/.72 Georgia,'Times New Roman',serif;color:rgba(73,67,59,.032)} +#sunCloudAuthGateV3.sun-cloud-auth-gate:after{content:'';position:fixed;z-index:0;right:-7vw;bottom:-14vh;width:min(48vw,720px);height:min(70vh,850px);pointer-events:none;background:radial-gradient(ellipse at 58% 20%,rgba(67,64,58,.14) 0 7%,transparent 8%),radial-gradient(ellipse at 36% 35%,rgba(67,64,58,.13) 0 6%,transparent 7%),radial-gradient(ellipse at 68% 47%,rgba(67,64,58,.12) 0 8%,transparent 9%),linear-gradient(106deg,transparent 43%,rgba(67,64,58,.08) 44% 46%,transparent 47%);filter:blur(18px);transform:rotate(-17deg);opacity:.45} +#sunCloudAuthGateV3 .sun-cloud-auth-card{position:relative!important;z-index:3!important;width:min(790px,100%)!important;margin:0!important;padding:0 0 58px!important;border:0!important;border-radius:0!important;background:transparent!important;color:#2c2924!important;box-shadow:none!important;backdrop-filter:none!important} +#sunCloudAuthGateV3 .caterium-signature-brand{display:flex;align-items:center;gap:13px;margin:0 0 clamp(46px,7vh,88px)!important}.caterium-signature-mark{display:block;width:62px;height:62px;object-fit:contain;flex:0 0 62px}.caterium-signature-word{font:500 36px/1 Georgia,'Times New Roman',serif;color:#26231f;letter-spacing:-.02em} +#sunCloudAuthGateV3 .sun-cloud-auth-brand{display:block!important;margin:0 0 35px!important}#sunCloudAuthGateV3 .sun-cloud-auth-brand img{display:none!important}#sunCloudAuthGateV3 .sun-cloud-auth-brand h2{margin:0!important;max-width:720px;color:#24211d!important;font:500 clamp(48px,5.2vw,76px)/.99 Georgia,'Times New Roman',serif!important;letter-spacing:-.045em!important}#sunCloudAuthGateV3 .sun-cloud-auth-brand .hint{margin-top:18px!important;color:#8e887f!important;font-size:clamp(16px,1.55vw,21px)!important;line-height:1.45!important;font-weight:400!important} +#sunCloudAuthGateV3 .sun-cloud-auth-card>p.hint{color:#817b72!important}#sunCloudAuthGateV3 .sun-cloud-auth-card label{display:block!important;margin:14px 0 0!important;color:#716b62!important;font-size:12px!important;font-weight:700!important}#sunCloudAuthGateV3 label.caterium-clean-field{font-size:0!important;color:transparent!important;position:relative!important} +#sunCloudAuthGateV3 .sun-cloud-auth-card input,#sunCloudAuthGateV3 .sun-cloud-auth-card select,#sunCloudAuthGateV3 .sun-cloud-auth-card textarea{width:100%!important;min-height:58px!important;margin-top:7px!important;padding:0 18px!important;border:1px solid #bdb5aa!important;border-radius:13px!important;background:rgba(255,255,255,.13)!important;color:#312e29!important;box-shadow:none!important;outline:none!important;font-size:16px!important;font-weight:500!important}#sunCloudAuthGateV3 label.caterium-clean-field input{margin-top:0!important;padding-left:58px!important}#sunCloudAuthGateV3 input::placeholder{color:#9c958c!important;opacity:1!important}#sunCloudAuthGateV3 input:focus{border-color:#d7a632!important;box-shadow:0 0 0 4px rgba(216,167,50,.12)!important;background:rgba(255,255,255,.5)!important} +#sunCloudAuthGateV3 .caterium-email-field:before{content:'✉';position:absolute;z-index:2;left:20px;top:15px;color:#777168;font-size:25px;font-weight:400}#sunCloudAuthGateV3 .caterium-password-field:before{content:'♙';position:absolute;z-index:2;left:21px;top:14px;color:#777168;font-size:24px;transform:rotate(180deg);opacity:.8} +#sunCloudAuthGateV3 .sun-auth-password{display:block!important;position:relative!important}#sunCloudAuthGateV3 .sun-auth-password input{padding-right:58px!important}#sunCloudAuthGateV3 .sun-auth-eye{right:9px!important;bottom:10px!important;width:38px!important;height:38px!important;color:#777168!important;background:transparent!important;border-radius:9px!important} +#sunCloudAuthGateV3 .sun-cloud-auth-actions{display:block!important;margin-top:25px!important}#sunCloudAuthGateV3 .sun-cloud-auth-actions .primary,#sunCloudAuthGateV3 #sunGateSubmitV3{width:100%!important;min-height:62px!important;border:0!important;border-radius:13px!important;background:linear-gradient(100deg,#d9aa42,#efca69)!important;color:#171512!important;font-size:18px!important;font-weight:900!important;box-shadow:0 14px 34px rgba(184,135,34,.14)!important}#sunCloudAuthGateV3 #sunGateSubmitV3:after{content:' →';font-size:24px;font-weight:500;margin-left:13px} +#sunCloudAuthGateV3 .sun-cloud-auth-actions .outline{width:100%!important;margin-top:9px!important;min-height:48px!important;border:1px solid #c9c1b6!important;border-radius:11px!important;background:rgba(255,255,255,.25)!important;color:#37332d!important}#sunCloudAuthGateV3 .sun-cloud-auth-error{min-height:19px!important;margin-top:10px!important;color:#a64c40!important;font-size:12px!important}#sunCloudAuthGateV3 .sun-auth-switch{margin:26px 0 0!important;padding-top:25px!important;border-top:1px solid #cfc7bc!important;color:#898279!important;font-size:14px!important;text-align:center!important}#sunCloudAuthGateV3 .sun-auth-switch button{border:0!important;background:transparent!important;color:#c18f24!important;text-decoration:none!important;font-weight:900!important} +.caterium-signature-caption{position:fixed;left:42px;bottom:31px;z-index:4;color:#9d968d;font-size:9px;letter-spacing:.31em;text-transform:uppercase;pointer-events:none}.caterium-signature-caption:before{content:'';display:block;width:48px;height:2px;margin-bottom:13px;background:#d3a43c}.caterium-signature-manifesto{position:fixed;right:56px;top:44px;z-index:4;width:270px;color:#a19a91;font-size:10px;line-height:1.9;letter-spacing:.28em;text-transform:uppercase;pointer-events:none;white-space:pre-line}.caterium-flow-top{position:fixed;z-index:1;right:5.5vw;top:13vh;width:min(37vw,470px);opacity:.86;pointer-events:none;color:#a7a097}.caterium-flow-top svg{width:100%;height:auto}.caterium-flow-top path{fill:none;stroke:currentColor;stroke-width:1.6}.caterium-flow-top circle{fill:currentColor}.caterium-flow-top circle.gold{fill:#e5a313} +@media(max-width:980px){#sunCloudAuthGateV3.sun-cloud-auth-gate{padding:34px 28px 70px!important;place-items:start center!important}#sunCloudAuthGateV3 .sun-cloud-auth-card{width:min(720px,100%)!important;padding-top:12px!important}.caterium-signature-manifesto{display:none!important}.caterium-flow-top{right:-60px;top:125px;width:360px;opacity:.45}} +@media(max-width:620px){body.sun-cloud-auth-required{overflow:auto!important}#sunCloudAuthGateV3.sun-cloud-auth-gate{min-height:100dvh!important;padding:25px 18px 50px!important;place-items:start center!important}#sunCloudAuthGateV3 .sun-cloud-auth-card{width:100%!important;padding:0!important}.caterium-signature-brand{margin-bottom:42px!important}.caterium-signature-mark{width:50px;height:50px;flex-basis:50px}.caterium-signature-word{font-size:29px}#sunCloudAuthGateV3 .sun-cloud-auth-brand h2{font-size:clamp(38px,12vw,52px)!important}.caterium-flow-top{right:-125px;top:100px;width:330px;opacity:.25}.caterium-signature-caption{display:none!important}} + +#sunCloudAuthGateV3 .sun-auth-eye{position:absolute;border:0;cursor:pointer;font-size:17px} +#sunCloudAuthGateV3 .sun-auth-switch button{cursor:pointer} diff --git a/public/core/login-signature-v1776.js b/public/core/login-signature-v1776.js index d5acf67..425f01c 100644 --- a/public/core/login-signature-v1776.js +++ b/public/core/login-signature-v1776.js @@ -1,30 +1,12 @@ (()=>{ 'use strict'; if(window.CateriumLoginSignatureV1776)return; -const VERSION='17.7.7-cream-login-v2'; +const VERSION='17.7.8-single-login'; const $=(s,r=document)=>r.querySelector(s); const boot=()=>$('#cateriumAuthBootV1776'); const removeBoot=()=>{const b=boot();if(b){b.classList.add('leave');setTimeout(()=>b.remove(),180)}}; const FLOW=''; -function styles(){if($('#caterium-login-signature-v1776-style'))return;const s=document.createElement('style');s.id='caterium-login-signature-v1776-style';s.textContent=` -body.sun-cloud-auth-required{overflow:hidden!important;background:#f5f0e7!important}body.sun-cloud-auth-required>header,body.sun-cloud-auth-required>.view{visibility:hidden!important} -#sunCloudAuthGateV3.sun-cloud-auth-gate{position:fixed!important;inset:0!important;z-index:20000!important;overflow:auto!important;display:grid!important;place-items:center!important;padding:clamp(32px,6vh,78px) clamp(24px,8vw,120px)!important;background:radial-gradient(circle at 18% 18%,rgba(255,255,255,.92),transparent 34%),radial-gradient(circle at 77% 22%,rgba(255,255,255,.5),transparent 28%),linear-gradient(135deg,#f8f4ec 0%,#f2ece1 56%,#f7f3eb 100%)!important;color:#282621!important} -#sunCloudAuthGateV3.sun-cloud-auth-gate:before{content:'C';position:fixed;z-index:0;left:-7vw;top:49%;transform:translateY(-50%);pointer-events:none;font:400 min(72vw,860px)/.72 Georgia,'Times New Roman',serif;color:rgba(73,67,59,.032)} -#sunCloudAuthGateV3.sun-cloud-auth-gate:after{content:'';position:fixed;z-index:0;right:-7vw;bottom:-14vh;width:min(48vw,720px);height:min(70vh,850px);pointer-events:none;background:radial-gradient(ellipse at 58% 20%,rgba(67,64,58,.14) 0 7%,transparent 8%),radial-gradient(ellipse at 36% 35%,rgba(67,64,58,.13) 0 6%,transparent 7%),radial-gradient(ellipse at 68% 47%,rgba(67,64,58,.12) 0 8%,transparent 9%),linear-gradient(106deg,transparent 43%,rgba(67,64,58,.08) 44% 46%,transparent 47%);filter:blur(18px);transform:rotate(-17deg);opacity:.45} -#sunCloudAuthGateV3 .sun-cloud-auth-card{position:relative!important;z-index:3!important;width:min(790px,100%)!important;margin:0!important;padding:0 0 58px!important;border:0!important;border-radius:0!important;background:transparent!important;color:#2c2924!important;box-shadow:none!important;backdrop-filter:none!important} -#sunCloudAuthGateV3 .caterium-signature-brand{display:flex;align-items:center;gap:13px;margin:0 0 clamp(46px,7vh,88px)!important}.caterium-signature-mark{display:block;width:62px;height:62px;object-fit:contain;flex:0 0 62px}.caterium-signature-word{font:500 36px/1 Georgia,'Times New Roman',serif;color:#26231f;letter-spacing:-.02em} -#sunCloudAuthGateV3 .sun-cloud-auth-brand{display:block!important;margin:0 0 35px!important}#sunCloudAuthGateV3 .sun-cloud-auth-brand img{display:none!important}#sunCloudAuthGateV3 .sun-cloud-auth-brand h2{margin:0!important;max-width:720px;color:#24211d!important;font:500 clamp(48px,5.2vw,76px)/.99 Georgia,'Times New Roman',serif!important;letter-spacing:-.045em!important}#sunCloudAuthGateV3 .sun-cloud-auth-brand .hint{margin-top:18px!important;color:#8e887f!important;font-size:clamp(16px,1.55vw,21px)!important;line-height:1.45!important;font-weight:400!important} -#sunCloudAuthGateV3 .sun-cloud-auth-card>p.hint{color:#817b72!important}#sunCloudAuthGateV3 .sun-cloud-auth-card label{display:block!important;margin:14px 0 0!important;color:#716b62!important;font-size:12px!important;font-weight:700!important}#sunCloudAuthGateV3 label.caterium-clean-field{font-size:0!important;color:transparent!important;position:relative!important} -#sunCloudAuthGateV3 .sun-cloud-auth-card input,#sunCloudAuthGateV3 .sun-cloud-auth-card select,#sunCloudAuthGateV3 .sun-cloud-auth-card textarea{width:100%!important;min-height:58px!important;margin-top:7px!important;padding:0 18px!important;border:1px solid #bdb5aa!important;border-radius:13px!important;background:rgba(255,255,255,.13)!important;color:#312e29!important;box-shadow:none!important;outline:none!important;font-size:16px!important;font-weight:500!important}#sunCloudAuthGateV3 label.caterium-clean-field input{margin-top:0!important;padding-left:58px!important}#sunCloudAuthGateV3 input::placeholder{color:#9c958c!important;opacity:1!important}#sunCloudAuthGateV3 input:focus{border-color:#d7a632!important;box-shadow:0 0 0 4px rgba(216,167,50,.12)!important;background:rgba(255,255,255,.5)!important} -#sunCloudAuthGateV3 .caterium-email-field:before{content:'✉';position:absolute;z-index:2;left:20px;top:15px;color:#777168;font-size:25px;font-weight:400}#sunCloudAuthGateV3 .caterium-password-field:before{content:'♙';position:absolute;z-index:2;left:21px;top:14px;color:#777168;font-size:24px;transform:rotate(180deg);opacity:.8} -#sunCloudAuthGateV3 .sun-auth-password{display:block!important;position:relative!important}#sunCloudAuthGateV3 .sun-auth-password input{padding-right:58px!important}#sunCloudAuthGateV3 .sun-auth-eye{right:9px!important;bottom:10px!important;width:38px!important;height:38px!important;color:#777168!important;background:transparent!important;border-radius:9px!important} -#sunCloudAuthGateV3 .sun-cloud-auth-actions{display:block!important;margin-top:25px!important}#sunCloudAuthGateV3 .sun-cloud-auth-actions .primary,#sunCloudAuthGateV3 #sunGateSubmitV3{width:100%!important;min-height:62px!important;border:0!important;border-radius:13px!important;background:linear-gradient(100deg,#d9aa42,#efca69)!important;color:#171512!important;font-size:18px!important;font-weight:900!important;box-shadow:0 14px 34px rgba(184,135,34,.14)!important}#sunCloudAuthGateV3 #sunGateSubmitV3:after{content:' →';font-size:24px;font-weight:500;margin-left:13px} -#sunCloudAuthGateV3 .sun-cloud-auth-actions .outline{width:100%!important;margin-top:9px!important;min-height:48px!important;border:1px solid #c9c1b6!important;border-radius:11px!important;background:rgba(255,255,255,.25)!important;color:#37332d!important}#sunCloudAuthGateV3 .sun-cloud-auth-error{min-height:19px!important;margin-top:10px!important;color:#a64c40!important;font-size:12px!important}#sunCloudAuthGateV3 .sun-auth-switch{margin:26px 0 0!important;padding-top:25px!important;border-top:1px solid #cfc7bc!important;color:#898279!important;font-size:14px!important;text-align:center!important}#sunCloudAuthGateV3 .sun-auth-switch button{border:0!important;background:transparent!important;color:#c18f24!important;text-decoration:none!important;font-weight:900!important} -.caterium-signature-caption{position:fixed;left:42px;bottom:31px;z-index:4;color:#9d968d;font-size:9px;letter-spacing:.31em;text-transform:uppercase;pointer-events:none}.caterium-signature-caption:before{content:'';display:block;width:48px;height:2px;margin-bottom:13px;background:#d3a43c}.caterium-signature-manifesto{position:fixed;right:56px;top:44px;z-index:4;width:270px;color:#a19a91;font-size:10px;line-height:1.9;letter-spacing:.28em;text-transform:uppercase;pointer-events:none;white-space:pre-line}.caterium-flow-top{position:fixed;z-index:1;right:5.5vw;top:13vh;width:min(37vw,470px);opacity:.86;pointer-events:none;color:#a7a097}.caterium-flow-top svg{width:100%;height:auto}.caterium-flow-top path{fill:none;stroke:currentColor;stroke-width:1.6}.caterium-flow-top circle{fill:currentColor}.caterium-flow-top circle.gold{fill:#e5a313} -@media(max-width:980px){#sunCloudAuthGateV3.sun-cloud-auth-gate{padding:34px 28px 70px!important;place-items:start center!important}#sunCloudAuthGateV3 .sun-cloud-auth-card{width:min(720px,100%)!important;padding-top:12px!important}.caterium-signature-manifesto{display:none!important}.caterium-flow-top{right:-60px;top:125px;width:360px;opacity:.45}} -@media(max-width:620px){body.sun-cloud-auth-required{overflow:auto!important}#sunCloudAuthGateV3.sun-cloud-auth-gate{min-height:100dvh!important;padding:25px 18px 50px!important;place-items:start center!important}#sunCloudAuthGateV3 .sun-cloud-auth-card{width:100%!important;padding:0!important}.caterium-signature-brand{margin-bottom:42px!important}.caterium-signature-mark{width:50px;height:50px;flex-basis:50px}.caterium-signature-word{font-size:29px}#sunCloudAuthGateV3 .sun-cloud-auth-brand h2{font-size:clamp(38px,12vw,52px)!important}.caterium-flow-top{right:-125px;top:100px;width:330px;opacity:.25}.caterium-signature-caption{display:none!important}} -`;document.head.appendChild(s)} function field(g,id,ph,type){const i=$('#'+id,g);if(!i)return;i.placeholder=ph;const l=i.closest('label');if(l)l.classList.add('caterium-clean-field',type==='email'?'caterium-email-field':'caterium-password-field')} function decorate(g){if(!g)return false;const c=$('.sun-cloud-auth-card',g);if(!c)return false;if(!$('.caterium-signature-brand',c)){const b=document.createElement('div');b.className='caterium-signature-brand';b.innerHTML='Caterium';c.prepend(b)}if(!$('.caterium-signature-caption',g)){const n=document.createElement('div');n.className='caterium-signature-caption';n.textContent='Caterium · вкус в деталях';g.appendChild(n)}if(!$('.caterium-signature-manifesto',g)){const n=document.createElement('div');n.className='caterium-signature-manifesto';n.textContent='Простые решения\nдля больших событий';g.appendChild(n)}if(!$('.caterium-flow-top',g)){const n=document.createElement('div');n.className='caterium-flow-top';n.innerHTML=FLOW;g.appendChild(n)}const t=$('#sunGateTitleV3',g),sub=$('#sunGateSubtitleV3',g);if(t&&t.textContent.trim()==='Вход в Caterium')t.textContent='Войти в рабочее пространство';if(sub&&sub.textContent.trim()==='Введите данные своего аккаунта')sub.textContent='Ваши заказы. Ваша команда. Ваш результат.';field(g,'sunGateEmailV3','Email','email');field(g,'sunGatePasswordV3','Пароль','password');field(g,'sunGatePassword2V27','Подтвердите пароль','password');removeBoot();return true} -function scan(){return decorate($('#sunCloudAuthGateV3'))}styles();scan();const o=new MutationObserver(scan);o.observe(document.documentElement,{childList:true,subtree:true,characterData:true});window.addEventListener('sun:cloud-state-applied',scan);const p=setInterval(()=>{if(scan()||!boot())clearInterval(p)},80);setTimeout(()=>{clearInterval(p);if(boot()&&!$('#sunCloudAuthGateV3'))removeBoot()},10000);window.CateriumLoginSignatureV1776=Object.freeze({VERSION,scan,removeBoot,disconnect:()=>{o.disconnect();clearInterval(p)}}) +function scan(){return decorate($('#sunCloudAuthGateV3'))}scan();const o=new MutationObserver(scan);o.observe(document.documentElement,{childList:true,subtree:true,characterData:true});window.addEventListener('sun:cloud-state-applied',scan);const p=setInterval(()=>{if(scan()||!boot())clearInterval(p)},80);setTimeout(()=>{clearInterval(p);if(boot()&&!$('#sunCloudAuthGateV3'))removeBoot()},10000);window.CateriumLoginSignatureV1776=Object.freeze({VERSION,scan,removeBoot,disconnect:()=>{o.disconnect();clearInterval(p)}}) })(); \ No newline at end of file diff --git a/public/core/performance.js b/public/core/performance.js index 0849c81..7612ec1 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-proposal-loading'; + const RELEASE='20260918-single-login'; 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(){ @@ -16,16 +16,23 @@ #cateriumAuthBootV1776 .cab-word{font:500 36px/1 Georgia,'Times New Roman',serif;color:#26231f;letter-spacing:-.02em} #cateriumAuthBootV1776 h1{margin:0;max-width:720px;color:#24211d;font:500 clamp(48px,5.2vw,76px)/.99 Georgia,'Times New Roman',serif;letter-spacing:-.045em} #cateriumAuthBootV1776 p{margin:18px 0 0;color:#8e887f;font:400 clamp(16px,1.55vw,21px)/1.45 Arial,sans-serif} - #cateriumAuthBootV1776 .cab-fields{margin-top:25px;display:grid;gap:14px}.cab-field{height:58px;border:1px solid #bdb5aa;border-radius:13px;background:rgba(255,255,255,.13)} - #cateriumAuthBootV1776 .cab-btn{height:62px;margin-top:11px;border-radius:13px;background:linear-gradient(100deg,#d9aa42,#efca69)} #cateriumAuthBootV1776 .cab-note{position:fixed;left:42px;bottom:31px;color:#9d968d;font:9px/1 Arial,sans-serif;letter-spacing:.31em;text-transform:uppercase;text-shadow:none} @media(max-width:900px){#cateriumAuthBootV1776{padding:34px 28px 70px;place-items:start center}#cateriumAuthBootV1776 .cab-wrap{width:min(720px,100%);padding-top:12px}} @media(max-width:620px){#cateriumAuthBootV1776{min-height:100dvh;padding:25px 18px 50px;place-items:start center}#cateriumAuthBootV1776 .cab-wrap{width:100%}#cateriumAuthBootV1776 .cab-brand{margin-bottom:42px}#cateriumAuthBootV1776 .cab-mark{width:50px;height:50px;flex-basis:50px}#cateriumAuthBootV1776 .cab-word{font-size:29px}#cateriumAuthBootV1776 h1{font-size:clamp(38px,12vw,52px)}#cateriumAuthBootV1776 .cab-note{display:none}} `;document.head.appendChild(style); - const el=document.createElement('div');el.id='cateriumAuthBootV1776';el.innerHTML='
Caterium

Войти в рабочее пространство

Ваши заказы. Ваша команда. Ваш результат.

Caterium · вкус в деталях
';document.documentElement.appendChild(el); + const el=document.createElement('div');el.id='cateriumAuthBootV1776';el.innerHTML='
Caterium

Открываю Caterium…

Проверяю сохранённый вход

Caterium · вкус в деталях
';document.documentElement.appendChild(el); } function loadLoginSignatureEarly(){if(window.CateriumLoginSignatureV1776||document.getElementById('cateriumLoginSignatureV1776Script'))return;const s=document.createElement('script');s.id='cateriumLoginSignatureV1776Script';s.src=`core/login-signature-v1776.js?v=${RELEASE}`;s.async=false;document.head.appendChild(s)} installAuthBoot();loadLoginSignatureEarly(); + // Boot cleanup is independent of the optional decorative script. + if(document.getElementById('cateriumAuthBootV1776')){ + const bootObserver=new MutationObserver(()=>{ + const boot=document.getElementById('cateriumAuthBootV1776'); + if(!boot){bootObserver.disconnect();return} + if(document.getElementById('sunCloudAuthGateV3')||(window.SunAdminRBACV3&&!document.body?.classList.contains('sun-cloud-auth-required'))){boot.remove();bootObserver.disconnect()} + }); + bootObserver.observe(document.documentElement,{childList:true,subtree:true,attributes:true,attributeFilter:['class']}); + } const critical=img=>img.closest('header,.brand,#sunCloudAuthGate,.sun-auth-gate')||img.id==='sunLoginLogo'||img.classList.contains('sun-live-catalog-logo'); const tune=img=>{if(!(img instanceof HTMLImageElement)||critical(img))return;if(!img.hasAttribute('loading'))img.loading='lazy';if(!img.hasAttribute('decoding'))img.decoding='async';if(!img.hasAttribute('fetchpriority'))img.setAttribute('fetchpriority','low');}; diff --git a/public/index.html b/public/index.html index a881b4f..ce3df8a 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 13394ca..f78ec59 100644 --- a/public/service-worker.js +++ b/public/service-worker.js @@ -1,8 +1,8 @@ -const CACHE='sun-catering-pwa-v95-20260918-proposal-loading'; -const VERSION='20260918-proposal-loading'; +const CACHE='sun-catering-pwa-v96-20260918-single-login'; +const VERSION='20260918-single-login'; 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}`, + `./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/login-signature-v1776.css?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}`, './offer-gallery/001.jpg','./offer-gallery/002.jpg', './catalog/001.jpg','./catalog/002.jpg','./catalog/003.jpg', './sun-logo.png','./caterium-login-logo.png','./caterium-mark-light.svg','./pwa-icon-192.png','./pwa-icon-512.png','./manifest.webmanifest', @@ -10,7 +10,7 @@ const CORE=[ ]; const CRITICAL_FRESH=new Set([ '/core/cloud-transport.js','/core/trial-demo.js','/core/proposal-layout.js', - '/core/banquet-menu.js','/core/access-policy.js','/core/import-archive.js','/core/company-branding.js','/core/brand-theme.js','/core/sun-safe.js','/core/performance.js','/core/account-center-v1780.js','/core/login-signature-v1776.js','/core/auth-security-v1774.js','/legacy/bootstrap.js','/app-runtime.js' + '/core/banquet-menu.js','/core/access-policy.js','/core/import-archive.js','/core/company-branding.js','/core/brand-theme.js','/core/sun-safe.js','/core/performance.js','/core/account-center-v1780.js','/core/login-signature-v1776.js','/core/login-signature-v1776.css','/core/auth-security-v1774.js','/legacy/bootstrap.js','/app-runtime.js' ]); self.addEventListener('install',event=>{ event.waitUntil(caches.open(CACHE).then(cache=>cache.addAll(CORE)).then(()=>self.skipWaiting())); diff --git a/tests/login-recovery.spec.mjs b/tests/login-recovery.spec.mjs index 42ca29c..dcd872a 100644 --- a/tests/login-recovery.spec.mjs +++ b/tests/login-recovery.spec.mjs @@ -1,6 +1,48 @@ import fs from 'node:fs'; import {test,expect} from '@playwright/test'; +async function delayedSessionApp(page,signedIn=true){ + await page.route('https://**',r=>r.abort()); + await page.addInitScript(signed=>{ + localStorage.setItem('sunCloudV2Config',JSON.stringify({workspaceId:signed?'startup-company':'',localWorkspaceId:signed?'startup-company':'',tenantStorageReady:true,autoSync:false})); + if(signed)localStorage.setItem('sb-startup-auth-token','stored-session-placeholder-without-credentials'); + localStorage.setItem('sunEnterpriseSettingsV1',JSON.stringify({rolesEnabled:true})); + localStorage.setItem('sunUsersV1',JSON.stringify([{id:'legacy-user',name:'Old local user',role:'owner',active:true,pinHash:'old-hash'}])); + window.supabase={createClient:()=>({ + auth:{onAuthStateChange:()=>({data:{subscription:{unsubscribe(){}}}}),getSession:()=>new Promise(resolve=>{window.finishSessionRestore=resolve}),getUser:async()=>({data:{user:null}})}, + rpc:async name=>({data:name==='sun_my_workspaces'?[{id:'startup-company',name:'Test company',role:'admin',is_active:true}]:null}), + channel:()=>({on(){return this},subscribe(){return this}}),removeChannel:()=>{} + })}; + window.loginFormSeen=false;window.legacyFormSeen=false;new MutationObserver(()=>{if(document.getElementById('sunGateEmailV3'))window.loginFormSeen=true;if(document.getElementById('sunLoginOverlay'))window.legacyFormSeen=true}).observe(document,{childList:true,subtree:true}); + },signedIn); + await page.goto('/index.html',{waitUntil:'domcontentloaded'}); + await page.waitForFunction(()=>Boolean(window.finishSessionRestore)); +} + +test('restoring an existing session never mounts a password form before opening the app',async({page})=>{ + await delayedSessionApp(page); + await expect(page.locator('#sunCloudAuthGateV3')).toHaveAttribute('data-auth-state','restoring'); + await expect(page.locator('#sunGateEmailV3')).toHaveCount(0);await expect(page.locator('body > header')).toBeHidden(); + await page.evaluate(()=>finishSessionRestore({data:{session:{user:{id:'startup-user',email:'test@example.invalid'}}},error:null})); + await expect(page.locator('#sunCloudAuthGateV3')).toHaveCount(0);await expect(page.locator('body > header')).toBeVisible(); + expect(await page.evaluate(()=>window.loginFormSeen)).toBe(false); + expect(await page.evaluate(()=>window.legacyFormSeen)).toBe(false); +}); + +test('the single current login is styled even when its decorative script is unavailable',async({page})=>{ + await page.route('**/core/login-signature-v1776.js*',r=>r.abort()); + await delayedSessionApp(page,false); + await page.evaluate(()=>finishSessionRestore({data:{session:null},error:null})); + await expect(page.locator('#sunGateEmailV3')).toBeVisible(); + await expect(page.locator('#sunGateTitleV3')).toHaveText('Войти в рабочее пространство'); + await expect(page.locator('#sunCloudAuthGateV3 .caterium-signature-brand')).toHaveCount(1); + await expect(page.locator('#sunCloudAuthGateV3 .sun-cloud-auth-card')).toHaveCSS('background-color','rgba(0, 0, 0, 0)'); + await expect(page.locator('#sunGateEmailV3')).toHaveCSS('border-radius','13px'); + await expect(page.locator('#sunLoginOverlay,#cateriumAuthBootV1776')).toHaveCount(0); + await page.locator('#sunGateSwitchV27').click();await expect(page.locator('#sunGateTitleV3')).toHaveText('Создать аккаунт Caterium'); + await page.locator('#sunGateSwitchV27').click();await expect(page.locator('#sunGateTitleV3')).toHaveText('Войти в рабочее пространство'); +}); + test('slow connections use a healthy route, allow longer saves and preserve caller cancellation',async({page})=>{ await page.route('**/index.html',r=>r.fulfill({contentType:'text/html',body:''})); await page.goto('/index.html');await page.addScriptTag({url:'/core/cloud-transport.js'}); diff --git a/tests/release-check.mjs b/tests/release-check.mjs index f7c39cf..2acfc23 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('v95-20260918-proposal-loading')&&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-proposal-loading')&&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('v96-20260918-single-login')&&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-single-login')&&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('v95-20260918-proposal-loading'),'release manifest points to current PWA cache'); +check(String(releaseManifest.pwaCache||'').includes('v96-20260918-single-login'),'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-proposal-loading'),'index cache bust is v17.7.3'); -check(sw.includes('v95-20260918-proposal-loading')&&sw.includes('data-layer-v1773.js')&&sw.includes('server-automation-v1770.js'),'PWA caches v17.7.3 client foundation modules'); +check(index.includes('20260918-single-login'),'index cache bust is v17.7.3'); +check(sw.includes('v96-20260918-single-login')&&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 f82b391..75a1fb7 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-proposal-loading')||!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-proposal-loading')||!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-single-login')||!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-single-login')||!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');