let accessBusy=false; function accessToken(){const bytes=crypto.getRandomValues(new Uint8Array(32));return 'ad_'+Array.from(bytes,b=>b.toString(16).padStart(2,'0')).join('');} async function accessPage(){ const view=document.querySelector('#view'); if(document.querySelector('#access-page'))return; view.innerHTML=`
EINSTELLUNGEN / ZUGANG

Dein Zugang. Deine Schlüssel.

Ein Administratorzugang für die Oberfläche und ein separater Token für API-Clients.

Oberflächenkennwort ändern

Alle bestehenden Browser-Sitzungen werden abgemeldet. Der API-Token bleibt gültig.

API-Token ändern

Vor dem Speichern kopieren. Der bisherige Token wird sofort ungültig. Bestehende Browser-Sitzungen bleiben angemeldet. Verwende in API-Clients: Authorization: Bearer <Token>.

Der Token berechtigt zum Dienststatus, Hardwareabruf und Demo-Start/Stopp. Er erlaubt keine Kennwort-, Token- oder Netzwerkeinstellungen. Weitere Benutzer und Rollen sind noch nicht enthalten.

`; try{const r=await fetch('/api/v1/auth/status'),s=await r.json();if(!s.authenticated){location.reload();return}document.querySelector('#access-info').textContent=`Administrator · Diese Instanz: ${s.location}`;document.querySelector('#token-info').textContent=s.api_token_configured?('API-Token eingerichtet'+(s.token_changed_at?' · geändert am '+new Date(s.token_changed_at*1000).toLocaleString('de-DE'):'')+'. Der Wert wird nicht angezeigt.'):'Noch kein API-Token eingerichtet. API-Clients haben keinen Zugang.';}catch{document.querySelector('#access-message').textContent='Zugangsstatus nicht erreichbar.'} async function save(kind,data,form){if(accessBusy)return;accessBusy=true;view.querySelectorAll('button').forEach(b=>b.disabled=true);try{const r=await fetch('/api/v1/auth/'+kind,{method:'POST',headers:{'Content-Type':'application/json','X-Athena-Deck':'1'},body:JSON.stringify(data)}),s=await r.json();if(r.status===401){location.reload();return}if(!r.ok)throw Error(s.error);form.reset();if(s.login_required){location.reload();return}document.querySelector('#access-message').textContent='Neuer API-Token gespeichert. Der alte Token ist jetzt ungültig.';document.querySelector('#token-info').textContent='API-Token gerade geändert. Der Wert wird nicht angezeigt.';}catch(e){document.querySelector('#access-message').textContent=e.message}finally{accessBusy=false;view.querySelectorAll('button').forEach(b=>b.disabled=false)}} document.querySelector('#password-form').onsubmit=e=>{e.preventDefault();const value=document.querySelector('#new-password').value;if(value!==document.querySelector('#repeat-password').value){document.querySelector('#access-message').textContent='Die neuen Kennwörter stimmen nicht überein.';return}save('password',{current_password:document.querySelector('#current-password').value,new_password:value},e.target)}; document.querySelector('#token-form').onsubmit=e=>{e.preventDefault();save('token',{current_password:document.querySelector('#token-password').value,new_token:document.querySelector('#new-token').value},e.target)}; document.querySelector('#token-generate').onclick=()=>{document.querySelector('#new-token').value=accessToken();document.querySelector('#access-message').textContent='Neuen Token vor dem Speichern kopieren.'}; document.querySelector('#token-show').onclick=()=>{const input=document.querySelector('#new-token');input.type=input.type==='password'?'text':'password';document.querySelector('#token-show').textContent=input.type==='password'?'Token anzeigen':'Token verbergen';}; document.querySelector('#token-copy').onclick=async()=>{try{const token=document.querySelector('#new-token').value;if(!token)throw Error();await navigator.clipboard.writeText(token);document.querySelector('#access-message').textContent='Token kopiert. Bitte sicher speichern.';}catch{document.querySelector('#access-message').textContent='Kopieren nicht möglich. Bitte einen eigenen Token aus dem Passwortmanager verwenden.'}}; const hub=document.createElement('section');hub.className='card';hub.innerHTML=`

Hugging Face · Modelldownloads

Zugang wird geprüft …

Für private oder freigabepflichtige Modelle: Hugging-Face-Token erstellen ↗ mit Leserechten für das gewünschte Repository. Die Freigabe beantragst du auf dessen Modellkarte mit demselben Konto.

Separater Zugang für Hugging Face, nicht der Deck-API-Token. Speicherung auf Athena in einer Datei mit Zugriff nur für den Dienstbenutzer (0600), unverschlüsselt. Der gespeicherte Wert wird nie an den Browser zurückgegeben. Bereits laufende Downloads können nach Entfernen weiterlaufen.

`; document.querySelector('#access-page').insertBefore(hub,document.querySelector('#access-message')); const hfStatus=hub.querySelector('#hf-status'); const describe=s=>s.configured?'Token gespeichert. Ob die Modellfreigabe und Leserechte passen, prüft Hugging Face beim Download.':'Kein Hugging-Face-Token gespeichert. Öffentliche Downloads bleiben möglich.'; try{const r=await fetch('/api/v1/huggingface');if(!r.ok)throw Error();hfStatus.textContent=describe(await r.json());}catch{hfStatus.textContent='Hugging-Face-Zugangsstatus nicht erreichbar.';} async function saveHub(token){hub.querySelectorAll('button').forEach(b=>b.disabled=true);try{const r=await fetch('/api/v1/huggingface',{method:'POST',headers:{'Content-Type':'application/json','X-Athena-Deck':'1'},body:JSON.stringify({token})}),s=await r.json();if(!r.ok)throw Error(s.error||'Speichern fehlgeschlagen.');hfStatus.textContent=describe(s);}catch(e){hfStatus.textContent=e.message;}finally{hub.querySelector('#hf-token').value='';hub.querySelectorAll('button').forEach(b=>b.disabled=false);}} hub.querySelector('#hf-form').onsubmit=e=>{e.preventDefault();saveHub(hub.querySelector('#hf-token').value.trim());}; hub.querySelector('#hf-remove').onclick=()=>saveHub(null); document.querySelector('#logout').onclick=async()=>{const r=await fetch('/api/v1/logout',{method:'POST',headers:{'X-Athena-Deck':'1'}});if(r.ok||r.status===401)location.reload();else document.querySelector('#access-message').textContent='Abmelden fehlgeschlagen. Bitte erneut versuchen.'}; }