Files
baiyue-logistics-chat/static/js/visitor.js
T

50 lines
2.5 KiB
JavaScript

// A local random guest key restores this browser's history, never customer order identity.
var VisitorSession = (function(){
const storageName = 'baiyue-visitor-key';
const nativeFetch = typeof window.fetch === 'function' ? window.fetch.bind(window) : fetch;
let ready = null, established = false;
function localKey(){
try{
const saved = localStorage.getItem(storageName);
if(saved && /^[0-9a-f]{64}$/.test(saved)) return saved;
const crypto = window.crypto;
if(!crypto || !crypto.getRandomValues) return null;
const bytes = crypto.getRandomValues(new Uint8Array(32));
const key = Array.from(bytes, value => value.toString(16).padStart(2, '0')).join('');
localStorage.setItem(storageName, key);
return key;
}catch(error){ return null; } // HttpOnly cookie can still retain this guest when storage is disabled.
}
function ensure(){
if(ready) return ready;
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), 10000);
ready = nativeFetch('/api/visitor/session', {method:'POST',credentials:'same-origin',
headers:{'Content-Type':'application/json'},body:JSON.stringify({key:localKey()}),signal:controller.signal})
.then(async response => {
const data = await response.json();
if(!response.ok || !data.ok || typeof data.key !== 'string' || !/^[0-9a-f]{64}$/.test(data.key)){
throw new Error('访客会话暂时无法建立,请刷新后重试。');
}
try{ localStorage.setItem(storageName, data.key); }catch(error){}
established = true;
}).catch(error => { ready = null; established = false; throw error; }).finally(() => clearTimeout(timer));
return ready;
}
function request(path, options){
const init = Object.assign({}, options, {credentials:'same-origin'});
function send(){
if(init.signal && init.signal.aborted) return Promise.reject(Object.assign(new Error('Aborted'), {name:'AbortError'}));
const session = ready;
return nativeFetch(path, init).then(async response => {
if(response.status !== 401) return response;
if(ready === session){ ready = null; established = false; }
await ensure();
return nativeFetch(path, init);
});
}
return established ? send() : ensure().then(send);
}
return {fetch:request};
})();