feat: API 密钥改用 sessionStorage,关闭标签页即消失,彻底解决跨会话旧 Key 残留
Co-authored-by: qinfendebingshuo <207279273+qinfendebingshuo@users.noreply.github.com>
This commit is contained in:
parent
a42675d2a5
commit
33e1f3a746
|
|
@ -221,7 +221,7 @@ footer{padding:12px 18px 16px;background:var(--s1);border-top:1px solid var(--bo
|
||||||
<div class="sc-logo">🌀</div>
|
<div class="sc-logo">🌀</div>
|
||||||
<h1>铸渊助手</h1>
|
<h1>铸渊助手</h1>
|
||||||
<p class="sub">HoloLake · 代码守护人格体 · 持续成长的 AI 伙伴</p>
|
<p class="sub">HoloLake · 代码守护人格体 · 持续成长的 AI 伙伴</p>
|
||||||
<div class="sc-sec">🔒 API 密钥只保存在你的浏览器本地,不会上传至任何服务器</div>
|
<div class="sc-sec">🔒 API 密钥仅本次会话有效,关闭标签页即自动清除,不会持久保存在任何地方</div>
|
||||||
|
|
||||||
<div class="fg">
|
<div class="fg">
|
||||||
<label>选择 AI 提供商</label>
|
<label>选择 AI 提供商</label>
|
||||||
|
|
@ -370,7 +370,7 @@ footer{padding:12px 18px 16px;background:var(--s1);border-top:1px solid var(--bo
|
||||||
<div class="sfg">
|
<div class="sfg">
|
||||||
<label>API 密钥</label>
|
<label>API 密钥</label>
|
||||||
<input type="password" id="ck" placeholder="留空 = 保持当前密钥不变;输入新密钥即替换">
|
<input type="password" id="ck" placeholder="留空 = 保持当前密钥不变;输入新密钥即替换">
|
||||||
<small id="ck-hint" style="color:var(--dim)">🔒 只保存在浏览器 localStorage,不上传任何服务器</small>
|
<small id="ck-hint" style="color:var(--dim)">🔒 密钥仅本次会话有效,关闭标签页自动清除</small>
|
||||||
</div>
|
</div>
|
||||||
<button class="sbtn2" onclick="saveSet()">💾 保存设置</button>
|
<button class="sbtn2" onclick="saveSet()">💾 保存设置</button>
|
||||||
|
|
||||||
|
|
@ -587,7 +587,7 @@ const _initBase = _initProv === 'custom'
|
||||||
: (PROVS[_initProv]?.base||'https://api.yunwu.ai/v1');
|
: (PROVS[_initProv]?.base||'https://api.yunwu.ai/v1');
|
||||||
|
|
||||||
const A = {
|
const A = {
|
||||||
key: ls('zy_key')||'',
|
key: sessionStorage.getItem('zy_key')||'',
|
||||||
base: _initBase,
|
base: _initBase,
|
||||||
mdl: ls('zy_mdl')||'gpt-4o',
|
mdl: ls('zy_mdl')||'gpt-4o',
|
||||||
prov: _initProv,
|
prov: _initProv,
|
||||||
|
|
@ -619,19 +619,16 @@ async function boot(){
|
||||||
// This is a reliable escape hatch when the UI cannot be navigated.
|
// This is a reliable escape hatch when the UI cannot be navigated.
|
||||||
if(new URLSearchParams(location.search).get('reset')==='1'){
|
if(new URLSearchParams(location.search).get('reset')==='1'){
|
||||||
RESET_KEYS.forEach(k => localStorage.removeItem(k));
|
RESET_KEYS.forEach(k => localStorage.removeItem(k));
|
||||||
|
sessionStorage.removeItem('zy_key');
|
||||||
A.key=''; A.base=PROVS['yunwu'].base; A.mdl='gpt-4o'; A.prov='yunwu'; A.demo=false;
|
A.key=''; A.base=PROVS['yunwu'].base; A.mdl='gpt-4o'; A.prov='yunwu'; A.demo=false;
|
||||||
A.userName=''; A.ghUser=''; A.role='guest';
|
A.userName=''; A.ghUser=''; A.role='guest';
|
||||||
// Remove ?reset=1 from the URL so a subsequent refresh does not trigger another reset.
|
// Remove ?reset=1 from the URL so a subsequent refresh does not trigger another reset.
|
||||||
history.replaceState(null,'',location.pathname);
|
history.replaceState(null,'',location.pathname);
|
||||||
}
|
}
|
||||||
// Sanitize: clear any stored key that contains the old KEY_MASK placeholder string.
|
// One-time migration: keys stored by old versions of this app in localStorage are
|
||||||
// Valid API keys are always alphanumeric (no Chinese characters), so this check
|
// no longer used (keys now live only in sessionStorage). Clear any stale value
|
||||||
// cannot produce false positives on a legitimate key.
|
// so it can never be accidentally picked up again.
|
||||||
const storedKey = ls('zy_key');
|
localStorage.removeItem('zy_key');
|
||||||
if(storedKey && storedKey.includes(KEY_MASK)){
|
|
||||||
localStorage.removeItem('zy_key');
|
|
||||||
A.key = '';
|
|
||||||
}
|
|
||||||
initSetupUI();
|
initSetupUI();
|
||||||
// Restore identity from localStorage
|
// Restore identity from localStorage
|
||||||
if(A.userName) A.userMeta = ROLE_MAP[A.userName]||null;
|
if(A.userName) A.userMeta = ROLE_MAP[A.userName]||null;
|
||||||
|
|
@ -699,7 +696,9 @@ function doSetup(){
|
||||||
let base = PROVS[pv]?.base||'';
|
let base = PROVS[pv]?.base||'';
|
||||||
if(pv==='custom') base=(document.getElementById('sep')?.value||'').trim()||base;
|
if(pv==='custom') base=(document.getElementById('sep')?.value||'').trim()||base;
|
||||||
A.key=k; A.base=base; A.mdl=md; A.prov=pv; A.demo=false;
|
A.key=k; A.base=base; A.mdl=md; A.prov=pv; A.demo=false;
|
||||||
lss('zy_key',k); lss('zy_base',base); lss('zy_mdl',md); lss('zy_prov',pv);
|
// Key is stored in sessionStorage only — it disappears when the tab/browser is closed.
|
||||||
|
// This prevents stale keys from persisting across sessions.
|
||||||
|
sessionStorage.setItem('zy_key', k); lss('zy_base',base); lss('zy_mdl',md); lss('zy_prov',pv);
|
||||||
// Save identity
|
// Save identity
|
||||||
const un = document.getElementById('suid').value;
|
const un = document.getElementById('suid').value;
|
||||||
const gh = (document.getElementById('sghuser')?.value||'').trim();
|
const gh = (document.getElementById('sghuser')?.value||'').trim();
|
||||||
|
|
@ -728,8 +727,8 @@ function initSettingsPanel(){
|
||||||
const hint = document.getElementById('ck-hint');
|
const hint = document.getElementById('ck-hint');
|
||||||
if(hint){
|
if(hint){
|
||||||
hint.textContent = A.key
|
hint.textContent = A.key
|
||||||
? '🔒 当前已保存 Key 末4位:…'+(A.key.length>=4?A.key.slice(-4):A.key)+' · 留空保持不变,直接输入新 Key 即可替换'
|
? '🕐 密钥本次会话有效(末4位:…'+(A.key.length>=4?A.key.slice(-4):A.key)+')· 留空保持不变,输入新值即替换 · 关闭标签页自动清除'
|
||||||
: '🔒 尚未设置 API 密钥,请输入后保存';
|
: '🔒 密钥仅本次会话有效,关闭标签页自动清除,请输入后保存';
|
||||||
}
|
}
|
||||||
if(pv==='custom'){
|
if(pv==='custom'){
|
||||||
document.getElementById('cep-g').style.display='block';
|
document.getElementById('cep-g').style.display='block';
|
||||||
|
|
@ -749,7 +748,9 @@ function saveSet(){
|
||||||
let base = PROVS[pv]?.base||'';
|
let base = PROVS[pv]?.base||'';
|
||||||
if(pv==='custom') base=(document.getElementById('cep').value.trim())||base;
|
if(pv==='custom') base=(document.getElementById('cep').value.trim())||base;
|
||||||
A.key=k; A.base=base; A.mdl=md; A.prov=pv; A.demo=!k;
|
A.key=k; A.base=base; A.mdl=md; A.prov=pv; A.demo=!k;
|
||||||
lss('zy_key',k); lss('zy_base',base); lss('zy_mdl',md); lss('zy_prov',pv);
|
// Key lives in sessionStorage only — cleared when tab/browser closes.
|
||||||
|
if(k){ sessionStorage.setItem('zy_key', k); } else { sessionStorage.removeItem('zy_key'); }
|
||||||
|
lss('zy_base',base); lss('zy_mdl',md); lss('zy_prov',pv);
|
||||||
// Save identity
|
// Save identity
|
||||||
const un = document.getElementById('cuid')?.value||A.userName;
|
const un = document.getElementById('cuid')?.value||A.userName;
|
||||||
const gh = document.getElementById('cghuser')?.value?.trim()||A.ghUser;
|
const gh = document.getElementById('cghuser')?.value?.trim()||A.ghUser;
|
||||||
|
|
@ -768,6 +769,7 @@ function saveSet(){
|
||||||
function resetAllSettings(){
|
function resetAllSettings(){
|
||||||
if(!confirm('确定要清除所有本地设置吗?\n\n· API 密钥会被删除\n· 身份设置会被删除\n· 将返回初始设置界面\n\n这不会影响任何聊天记录或云端数据。')) return;
|
if(!confirm('确定要清除所有本地设置吗?\n\n· API 密钥会被删除\n· 身份设置会被删除\n· 将返回初始设置界面\n\n这不会影响任何聊天记录或云端数据。')) return;
|
||||||
RESET_KEYS.forEach(k => localStorage.removeItem(k));
|
RESET_KEYS.forEach(k => localStorage.removeItem(k));
|
||||||
|
sessionStorage.removeItem('zy_key');
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue