caterium-app/public/core/auth-security-v1774.js
2026-09-13 18:43:54 +03:00

66 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

(()=>{
'use strict';
const VERSION='17.8.1-auth-security-promo';
const PENDING_REGISTRATION_KEY='sunPendingRegistrationV23';
let busy=false;
const $=id=>document.getElementById(id);
const cloud=()=>window.SunCloudV2||null;
const client=()=>cloud()?.getClient?.()||null;
const esc=value=>window.SunSafe?.escapeHTML?window.SunSafe.escapeHTML(String(value??'')):String(value??'');
function redirectUrl(){try{const u=new URL(location.href);u.hash='';return u.toString();}catch(_){return location.href.split('#')[0];}}
function savePendingRegistration(email,companyName,promoCode){try{localStorage.setItem(PENDING_REGISTRATION_KEY,JSON.stringify({email:String(email||'').trim().toLowerCase(),companyName:String(companyName||'').trim()||'Новая компания',promoCode:String(promoCode||'').trim().toUpperCase(),createdAt:new Date().toISOString()}));}catch(_){}}
function clearPendingRegistration(){try{localStorage.removeItem(PENDING_REGISTRATION_KEY)}catch(_){}}
function setError(root,message){const node=root?.querySelector?.('#sunGateErrorV3');if(node)node.textContent=String(message||'');}
function friendlySignupError(error){const message=String(error?.message||error||'Не удалось создать аккаунт.');if(/email address not authorized/i.test(message))return 'Не удалось отправить письмо подтверждения. Обратитесь к администратору Caterium.';if(/rate limit|rate_limit|too many/i.test(message))return 'Слишком много попыток регистрации. Попробуйте немного позже.';return message;}
async function signOutUnsafeSession(c){try{await c?.auth?.signOut?.()}catch(_){}}
function ensurePromoField(){
const fields=$('sunGateRegisterFieldsV27');if(!fields||$('sunGatePromoV181'))return;
const company=$('sunGateCompanyV3');const label=document.createElement('label');label.id='sunGatePromoLabelV181';label.innerHTML='<span>Промокод пробной версии</span><input id="sunGatePromoV181" autocomplete="off" maxlength="32" placeholder="Например, CTM-A7K4P9"><small id="sunGatePromoHintV181" style="display:block;margin-top:5px;opacity:.72">Промокод выдаёт разработчик Caterium.</small>';
if(company?.parentElement)company.parentElement.insertAdjacentElement('afterend',label);else fields.appendChild(label);
const input=$('sunGatePromoV181');input?.addEventListener('input',()=>{input.value=input.value.toUpperCase().replace(/\s+/g,'');const h=$('sunGatePromoHintV181');if(h)h.textContent='Промокод выдаёт разработчик Caterium.';});
input?.addEventListener('blur',async()=>{const code=String(input.value||'').trim(),email=String($('sunGateEmailV3')?.value||'').trim().toLowerCase(),h=$('sunGatePromoHintV181');if(!code||!h)return;try{const c=client();if(!c)return;const {data,error}=await c.rpc('caterium_trial_promo_preview',{p_code:code,p_email:email||null});if(error)throw error;h.textContent=data?.valid?`Промокод принят · ${Number(data.trial_days||14)} дней пробного доступа`:(data?.reason||'Промокод недействителен');}catch(_){h.textContent='Не удалось проверить промокод.';}});
}
async function validatePromo(c,email,code){
if(!code)throw new Error('Введите промокод пробной версии Caterium.');
const {data,error}=await c.rpc('caterium_trial_promo_preview',{p_code:code,p_email:email});if(error)throw error;
if(!data?.valid)throw new Error(data?.reason||'Промокод недействителен.');return data;
}
function showPublicConfirmation(gate,email){if(!gate)return;gate.innerHTML=`<div class="sun-cloud-auth-card"><div class="sun-cloud-auth-brand"><img src="caterium-login-logo.png" alt="Caterium"><div><h2>Подтвердите email</h2><div class="hint">Безопасная регистрация Caterium</div></div></div><p class="hint">Мы отправили письмо на <b>${esc(email)}</b>. Откройте письмо и нажмите ссылку подтверждения. После подтверждения Caterium откроется автоматически и активирует пробную версию по вашему промокоду.</p><div class="sun-cloud-auth-actions"><button class="primary" id="sunAuthGoLoginV1774" type="button">Перейти ко входу</button></div><div class="sun-cloud-auth-error" id="sunGateErrorV3"></div></div>`;gate.querySelector('#sunAuthGoLoginV1774')?.addEventListener('click',()=>{gate.remove();document.body.classList.remove('sun-cloud-auth-required');setTimeout(()=>window.SunEnterprise?.ensureAuthGate?.(),30);location.reload();},{once:true});}
function showInviteConfirmation(gate,email){if(!gate)return;gate.innerHTML=`<div class="sun-cloud-auth-card"><div class="sun-cloud-auth-brand"><img src="caterium-login-logo.png" alt="Caterium"><div><h2>Подтвердите email</h2><div class="hint">Приглашение сохранено</div></div></div><p class="hint">Письмо подтверждения отправлено на <b>${esc(email)}</b>. Подтвердите адрес по ссылке в письме. После возврата в Caterium приглашение останется доступно.</p><div class="sun-cloud-auth-actions"><button class="primary" id="sunInviteReloadV1774" type="button">Я подтвердил email</button></div><div class="sun-cloud-auth-error" id="sunGateErrorV3"></div></div>`;gate.querySelector('#sunInviteReloadV1774')?.addEventListener('click',()=>location.reload(),{once:true});}
async function publicSignup(gate){
if(busy)return;const c=client();if(!c){setError(gate,'Облачный сервис не подключён.');return;}
const email=String($('sunGateEmailV3')?.value||'').trim().toLowerCase(),password=String($('sunGatePasswordV3')?.value||''),password2=String($('sunGatePassword2V27')?.value||''),companyName=String($('sunGateCompanyV3')?.value||'').trim(),promoCode=String($('sunGatePromoV181')?.value||'').trim().toUpperCase();
if(!companyName){setError(gate,'Введите название компании.');return;}if(!email||password.length<6){setError(gate,'Введите email и пароль минимум из 6 символов.');return;}if(password!==password2){setError(gate,'Пароли не совпадают.');return;}
busy=true;const button=$('sunGateSubmitV3');if(button)button.disabled=true;setError(gate,'Проверяю промокод…');
try{
await validatePromo(c,email,promoCode);setError(gate,'Создаю безопасный аккаунт…');savePendingRegistration(email,companyName,promoCode);
const existing=await c.auth.signInWithPassword({email,password});if(!existing.error&&existing.data?.session){location.reload();return;}
const result=await c.auth.signUp({email,password,options:{emailRedirectTo:redirectUrl(),data:{company_name:companyName,promo_code:promoCode}}});if(result.error)throw result.error;
if(result.data?.session){await signOutUnsafeSession(c);throw new Error('Защита email ещё не активирована на сервере. Регистрация остановлена, чтобы не создавать неподтверждённый аккаунт.');}
showPublicConfirmation(gate,email);
}catch(error){clearPendingRegistration();setError(gate,friendlySignupError(error));if(button)button.disabled=false;}finally{busy=false;}
}
async function acceptInviteAfterLogin(token,gate){const api=cloud();const ws=await api?.acceptInvite?.(token);if(!ws)throw new Error('Не удалось присоединиться к компании.');try{const u=new URL(location.href);u.searchParams.delete('invite');history.replaceState(null,'',u.toString())}catch(_){}location.reload();}
async function inviteSignup(gate){
if(busy)return;const c=client();if(!c){setError(gate,'Облачный сервис не подключён.');return;}
const email=String($('sunInviteEmailV27')?.value||'').trim().toLowerCase(),name=String($('sunInviteNameV27')?.value||'').trim(),password=String($('sunInvitePasswordV27')?.value||''),password2=String($('sunInvitePassword2V27')?.value||'');let token='';try{token=String(new URL(location.href).searchParams.get('invite')||'').trim()}catch(_){}
if(!token){setError(gate,'Ссылка приглашения повреждена.');return;}if(!email||password.length<6){setError(gate,'Пароль должен содержать минимум 6 символов.');return;}if(password!==password2){setError(gate,'Пароли не совпадают.');return;}
busy=true;const button=$('sunInviteJoinV27');if(button)button.disabled=true;setError(gate,'Проверяю аккаунт…');
try{const existing=await c.auth.signInWithPassword({email,password});if(!existing.error&&existing.data?.session){await acceptInviteAfterLogin(token,gate);return;}const result=await c.auth.signUp({email,password,options:{emailRedirectTo:redirectUrl(),data:{name}}});if(result.error)throw result.error;if(result.data?.session){await signOutUnsafeSession(c);throw new Error('Защита email ещё не активирована на сервере. Приглашение остановлено, чтобы не создавать неподтверждённый аккаунт.');}showInviteConfirmation(gate,email);}catch(error){setError(gate,friendlySignupError(error));if(button)button.disabled=false;}finally{busy=false;}
}
function publicRegistrationMode(){const fields=$('sunGateRegisterFieldsV27');return Boolean(fields&&!fields.hidden);}
document.addEventListener('click',event=>{const target=event.target instanceof Element?event.target:null;if(!target)return;if(target.closest('#sunGateSubmitV3')&&publicRegistrationMode()){event.preventDefault();event.stopImmediatePropagation();publicSignup($('sunCloudAuthGateV3')).catch(error=>setError($('sunCloudAuthGateV3'),friendlySignupError(error)));return;}if(target.closest('#sunInviteJoinV27')){event.preventDefault();event.stopImmediatePropagation();inviteSignup($('sunCloudAuthGateV3')).catch(error=>setError($('sunCloudAuthGateV3'),friendlySignupError(error)));}},true);
document.addEventListener('keydown',event=>{if(event.key!=='Enter')return;const target=event.target instanceof Element?event.target:null;if(target?.id==='sunGatePassword2V27'&&publicRegistrationMode()){event.preventDefault();event.stopImmediatePropagation();publicSignup($('sunCloudAuthGateV3')).catch(error=>setError($('sunCloudAuthGateV3'),friendlySignupError(error)));}else if(target?.id==='sunInvitePassword2V27'){event.preventDefault();event.stopImmediatePropagation();inviteSignup($('sunCloudAuthGateV3')).catch(error=>setError($('sunCloudAuthGateV3'),friendlySignupError(error)));}},true);
const promoObserver=new MutationObserver(()=>ensurePromoField());promoObserver.observe(document.documentElement,{childList:true,subtree:true});if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',ensurePromoField,{once:true});else ensurePromoField();
window.CateriumAuthSecurityV1774=Object.freeze({VERSION,redirectUrl,ensurePromoField});
})();