@@ -84,6 +84,7 @@ function authKey(value){return isRedactedSecret(value)?'':String(value||'').trim
8484function currentBridgeAuthKey(){return authKey(cfg?.bridgeApiKey)||fullAdminAuthKey()||authKey(localStorage.getItem('bridgeApiKey'))||'';}
8585function auth(){const key=currentBridgeAuthKey()||(!pendingBridgeKey?fullBridgeKey():'')||''; return key?{'authorization':'Bearer '+key,'content-type':'application/json'}:{'content-type':'application/json'}}
8686function duplicateCredentialMessage(e){const text=String(e?.message||e||''); return text.includes('duplicate_commandcode_api_key')||text.includes('Duplicate CommandCode API key')?tr('duplicateSave'):null;}
87+ function saveErrorMessage(e){const text=String(e?.message||e||''); try{const j=JSON.parse(text); const msg=j?.error?.message; if(typeof msg==='string'&&msg)return msg;}catch{} return text;}
8788function preventDuplicateVisibleKey(input){const value=input.value.trim(); if(!value)return false; const dup=Array.from(document.querySelectorAll('[data-ckey]')).some(el=>el!==input&&el.value.trim()===value); if(!dup)return false; const i=+input.dataset.ckey; input.value=''; if(cfg?.credentials?.[i])cfg.credentials[i].apiKey=''; toast(tr('duplicateInput')); return true;}
8889function syncBridgeKey(){const el=$('bridgeApiKey'); if(!el)return; const configured=authKey(cfg?.bridgeApiKey); const stored=authKey(localStorage.getItem('bridgeApiKey')); const key=pendingBridgeKey||configured||stored; if(configured&&stored!==configured)localStorage.setItem('bridgeApiKey',configured); if(document.activeElement!==el) el.value=displayBridgeKey(key);}
8990function syncAdminAuthKey(){const wrap=$('adminAuthWrap'); if(!wrap)return; wrap.hidden=!cfg?.bridgeApiKey;}
@@ -106,7 +107,7 @@ function generateBridgeKey(){const key=randomBridgeKey(); rememberPendingBridgeK
106107$('bindHost').onchange=()=>{cfg.server.host=$('bindHost').value;syncBridgeKey();setDirty();}; $('bindPort').oninput=()=>{cfg.server.port=Number($('bindPort').value)||9992;setDirty();}; function saveBridgeKey(nextKey){const key=nextKey||fullBridgeKey(); const current=currentBridgeAuthKey(); if(key&&key!==current){rememberPendingBridgeKey(key); syncBridgeKey(); setDirty(); toast(tr('pendingKeySaved')); return;} if(key)localStorage.setItem('bridgeApiKey',key); else localStorage.removeItem('bridgeApiKey'); syncBridgeKey(); toast(key?tr('currentKeySaved'):tr('clientKeyCleared'));} async function copyBridgeKey(){const key=fullBridgeKey()||authKey(localStorage.getItem('bridgeApiKey'))||''; if(!key){toast(tr('clientKeyEmpty'));return;} try{await navigator.clipboard.writeText(key);toast(tr('clientKeyCopied'));}catch{toast(tr('copyFailed'));}} $('generateBridgeKey').onclick=generateBridgeKey; $('saveBridgeKey').onclick=()=>saveBridgeKey(); $('copyBridgeKey').onclick=copyBridgeKey; $('bridgeApiKey').onkeydown=e=>{if(e.key==='Enter')saveBridgeKey();}; $('maxPer').oninput=()=>{cfg.routing.maxInFlightPerCredential=Number($('maxPer').value)||4; setDirty();}; $('refreshCreds').onclick=async()=>{try{const m=await api('/admin/commandcode/credentials?refresh=true'); const byId=new Map((m.credentials||[]).map(x=>[x.id,x])); cfg.credentials.forEach(c=>c.metrics=byId.get(c.id)); render(); toast(tr('credsRefreshed'));}catch(e){toast(tr('credRefreshFailed'));}}; $('addCred').onclick=()=>{cfg.credentials.push({id:'key'+(cfg.credentials.length+1),apiKey:'',weight:1,enabled:true});render();setDirty();};
107108$('adminAuthKey').onchange=()=>{const key=fullAdminAuthKey(); if(key)localStorage.setItem('bridgeApiKey',key);};
108109function credentialPayloads(){const originalIds=new Map(); document.querySelectorAll('[data-cid]').forEach(e=>{const i=+e.dataset.cid; if(cfg.credentials[i]){originalIds.set(i,e.dataset.originalId||cfg.credentials[i].originalId||cfg.credentials[i].id||('key'+(i+1))); cfg.credentials[i].id=e.value.trim()||('key'+(i+1));}}); document.querySelectorAll('[data-cenabled]').forEach(e=>{const i=+e.dataset.cenabled; if(cfg.credentials[i])cfg.credentials[i].enabled=e.checked;}); document.querySelectorAll('[data-ckey]').forEach(e=>{const i=+e.dataset.ckey; if(cfg.credentials[i]&&e.value.trim())cfg.credentials[i].apiKey=e.value.trim();}); return cfg.credentials.map((c,i)=>{const keyEl=document.querySelector('[data-ckey="'+i+'"]'); const typedKey=keyEl?.value?.trim(); const originalId=originalIds.get(i)||c.originalId||c.id; return {id:c.id,originalId,apiKey:typedKey||c.apiKey||undefined,weight:c.weight||1,enabled:expiredIds.has(c.id)?undefined:c.enabled!==false,maxInFlight:c.maxInFlight,allowedModels:c.allowedModels};});}
109- $('save').onclick=async()=>{try{if(!cfg)throw new Error(tr('configNotLoaded')); cfg.server.host=$('bindHost').value; cfg.server.port=Number($('bindPort').value)||9992; cfg.routing.maxInFlightPerCredential=Number($('maxPer').value)||4; const checkedPolicy=document.querySelector('input[name=policy]:checked'); if(checkedPolicy)cfg.routing.policy=checkedPolicy.value; const pendingKey=pendingBridgeKey||((fullBridgeKey()&&fullBridgeKey()!==currentBridgeAuthKey())?fullBridgeKey():''); const payload={...(pendingKey?{bridgeApiKey:pendingKey}:{}),server:cfg.server,routing:cfg.routing,models:cfg.models,credentials:credentialPayloads()}; cfg=await api('/admin/config',{method:'PUT',body:JSON.stringify(payload)}); render(); setDirty(true); popup(tr('jsonSaved'),3000);}catch(e){popup(duplicateCredentialMessage(e)||(tr('saveFailed')+(e?.message|| e)),3000);}};
110+ $('save').onclick=async()=>{try{if(!cfg)throw new Error(tr('configNotLoaded')); cfg.server.host=$('bindHost').value; cfg.server.port=Number($('bindPort').value)||9992; cfg.routing.maxInFlightPerCredential=Number($('maxPer').value)||4; const checkedPolicy=document.querySelector('input[name=policy]:checked'); if(checkedPolicy)cfg.routing.policy=checkedPolicy.value; const pendingKey=pendingBridgeKey||((fullBridgeKey()&&fullBridgeKey()!==currentBridgeAuthKey())?fullBridgeKey():''); const payload={...(pendingKey?{bridgeApiKey:pendingKey}:{}),server:cfg.server,routing:cfg.routing,models:cfg.models,credentials:credentialPayloads()}; cfg=await api('/admin/config',{method:'PUT',body:JSON.stringify(payload)}); render(); setDirty(true); popup(tr('jsonSaved'),3000);}catch(e){popup(duplicateCredentialMessage(e)||(tr('saveFailed')+saveErrorMessage( e)),3000);}};
110111async function waitForRestart(){for(let elapsed=5;elapsed<=30;elapsed+=5){showPopup(tr('restartRequested')+elapsed+'s / 30s...',0); await new Promise(r=>setTimeout(r,5000)); const state=await load(); if(state?.ok&&!state.dirty&&!state.restartRequired){hidePopup(); return true;} if(elapsed<30)showPopup(tr('restartStill')+(elapsed+5)+tr('restartStillSuffix'),0);} popup(tr('restartTimeout'),5000); return false;}
111112$('restart').onclick=async()=>{const pendingKey=pendingBridgeKey; try{await api('/admin/restart',{method:'POST',body:'{}'}); if(pendingKey){localStorage.setItem('bridgeApiKey',pendingKey); rememberPendingBridgeKey('');} await waitForRestart();}catch(e){popup(tr('restartFailed')+(e?.message||e),3000);}};
112113applyLang();
0 commit comments