'}}catch(e){toast(e.message)}}
async function selectAgent(id,quiet,preserve){let same=selected===id;selected=id;try{current=await api('/api/v1/agents/'+encodeURIComponent(id));renderDetail(current,!!preserve&&same)}catch(e){if(!quiet)toast(e.message)}}
function renderPolicyHistory(){let rows=(policyHistory||[]).slice().reverse().slice(0,250);$('policyHistory').innerHTML=rows.length?'
Zeit
Ziel
Revision
Akteur
'+rows.map(x=>'
'+when(x.created_at)+'
'+esc(x.target)+'
'+esc(x.revision)+'
'+esc(x.actor||'–')+'
').join('')+'
':'
Noch keine Policy-Versionen.
'}
function renderAudit(rows){rows=(rows||[]).slice().reverse().slice(0,250);$('audit').innerHTML=rows.length?'
'}
function overviewStats(a){let s=a.snapshot||{},v=s.server||{},h=s.health||{},mem=v.memory_total?((v.memory_total-v.memory_available)*100/v.memory_total):0;return'
'}
function controlHTML(a){let tags=Object.entries(a.tags||{}).map(([k,v])=>k+'='+v).join('\n');return'
'}
async function sessionAction(id,action){if(!selected)return;let body={action};if(action==='message'){let m=prompt('Nachricht an Sitzung '+id+':');if(!m)return;body.message=m;body.title='SessionGuard'}if(action==='logoff'&&!confirm('Sitzung '+id+' wirklich abmelden? Die Profilsicherung startet nach dem Sitzungsende.'))return;try{await api('/api/v1/agents/'+encodeURIComponent(selected)+'/sessions/'+id+'/action',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)});toast('Sitzungsaktion an Agent gesendet');setTimeout(()=>selectAgent(selected,true,true),1000)}catch(e){toast(e.message)}}
function sessionsHTML(a){let s=a.snapshot||{},ss=s.sessions||[],p=(a.desired_policy&&a.desired_policy.revision)?a.desired_policy:s.policy,control=p&&p.sessions&&p.sessions.control_enabled;return ss.length?'
ID
Benutzer
Status
Logon / Idle
Client
Aktionen
'+ss.map(x=>'
'+x.id+'
'+esc((x.domain?x.domain+'\\':'')+x.user)+'
'+esc(x.state)+'
'+esc(when(x.logon_at))+' Idle '+Math.round((x.idle_seconds||0)/60)+' min
'}
function processesHTML(a){let ps=((a.snapshot||{}).processes||[]).filter(p=>p.session_id!==0).sort((x,y)=>y.memory_bytes-x.memory_bytes).slice(0,300);return ps.length?'
PID
Session
Prozess
RAM
'+ps.map(p=>'
'+p.pid+'
'+p.session_id+'
'+esc(p.name)+'
'+bytes(p.memory_bytes)+'
').join('')+'
':'
Keine Session-Prozesse erfasst.
'}
function telemetryHTML(a){let rows=Object.values(((a.snapshot||{}).telemetry)||{}).sort((x,y)=>new Date(y.first_seen_at)-new Date(x.first_seen_at)).slice(0,100);return rows.length?'
Session
Benutzer
Logon erkannt
Restore
Bis Ready
'+rows.map(x=>'
'+x.session_id+'
'+esc(x.user||x.sid||'–')+'
'+when(x.logon_at||x.first_seen_at)+'
'+((x.restore_duration_ms||0)/1000).toFixed(2)+' s
'+((x.observed_logon_ms||0)/1000).toFixed(2)+' s
').join('')+'
':'
Noch keine Logon-Telemetrie.
'}
function renderHistory(rows){rows=(rows||[]).slice().reverse();$('history').innerHTML=rows.length?'
Zeit
Server
Benutzer
Ereignis
Client
Details
'+rows.map(x=>'
'+when(x.time)+'
'+esc(x.hostname)+'
'+esc(x.user)+'
'+esc(x.event)+'
'+esc(x.client_name||'–')+'
'+esc(x.details||'–')+'
').join('')+'
':'
Noch keine Session-Historie.
'}
function renderAlerts(rows){$('alerts').innerHTML=rows.length?'
'}
function eventsHTML(a){let e=((a.snapshot||{}).events||[]).slice().reverse().slice(0,160);return e.length?'
Zeit
Typ
Benutzer
Meldung
'+e.map(x=>'
'+when(x.time)+'
'+esc(x.level)+'
'+esc(x.user||'–')+'
'+esc(x.message)+'
').join('')+'
':'
Keine Ereignisse.
'}
function activePolicy(a){if(a.desired_policy&&a.desired_policy.revision)return a.desired_policy;if(a.snapshot&&a.snapshot.policy)return a.snapshot.policy;return{cleanup:{grace_seconds:600,poll_seconds:10,retry_seconds:60,dry_run:true,allowed_profile_roots:['C:\\Users']},profiles:{retry_seconds:60,keep_versions:2,folders:[]},sessions:{disconnected_timeout_seconds:3600},templates:[]}}
function renderDetail(a,preserve){$('detailTitle').textContent=a.name||'Server';if(!preserve||editorAgent!==a.id||!$('overviewStats')){let p=activePolicy(a);policyTemplates=JSON.parse(JSON.stringify(p.templates||[]));profileFolders=JSON.parse(JSON.stringify((p.profiles&&p.profiles.folders)||[]));$('detail').className='';$('detail').innerHTML='
Serversteuerung
Sitzungen
Logon Performance
Prozesse
Profil-Pipeline
Agent-Ereignisse
Policy
'+policyForm(p,a)+'
';$('controlEditor').innerHTML=controlHTML(a);editorAgent=a.id;renderTemplates();renderProfileFolders()}$('overviewStats').innerHTML=overviewStats(a);$('sessionList').innerHTML=sessionsHTML(a);$('telemetryList').innerHTML=telemetryHTML(a);$('processList').innerHTML=processesHTML(a);$('profileList').innerHTML=profileHTML(a);$('eventList').innerHTML=eventsHTML(a)}
function templateDefault(){return{id:'neues-template',kind:'file',target:'Desktop\\Beispiel.txt',source:'',content:'',content_base64:'',url:'',shortcut:{target:'',arguments:'',working_directory:'',icon_location:'',description:''},overwrite:true}}function templateSpecific(t){let k=(t.kind||'file').toLowerCase();if(k==='directory')return'
Keine weiteren Angaben.
';if(k==='url')return'';if(k==='shortcut'){let s=t.shortcut||{};return''}return''}
function renderTemplates(){let h=$('templateList');if(!h)return;h.innerHTML=policyTemplates.length?policyTemplates.map((t,i)=>'
'+esc(t.id||('Template '+(i+1)))+'
'+templateSpecific(t)+'
').join(''):'
Keine Templates.
'}
function collectTemplates(){return[...document.querySelectorAll('.template-card')].map(c=>{let g=n=>{let e=c.querySelector('[data-field="'+n+'"]');return e?e.value:''},k=g('kind')||'file',t={id:g('id').trim(),kind:k,target:g('target').trim(),overwrite:!!c.querySelector('[data-field="overwrite"]:checked')};if(k==='file'){t.source=g('source').trim();t.content=g('content');t.content_base64=g('content_base64').trim()}else if(k==='url')t.url=g('url').trim();else if(k==='shortcut')t.shortcut={target:g('shortcut.target').trim(),arguments:g('shortcut.arguments'),working_directory:g('shortcut.working_directory').trim(),icon_location:g('shortcut.icon_location').trim(),description:g('shortcut.description')};return t})}
function renderProfileFolders(){let h=$('profileFolderList');if(!h)return;h.innerHTML=profileFolders.length?profileFolders.map((f,i)=>'
'+esc(f.path||('Ordner '+(i+1)))+'
').join(''):'
Keine Profilordner.
'}
function collectProfileFolders(){return[...document.querySelectorAll('.profile-folder')].map(c=>({path:c.querySelector('[data-field="path"]').value.trim(),exclude_globs:c.querySelector('[data-field="exclude"]').value.split('\n').map(x=>x.trim()).filter(Boolean)})).filter(x=>x.path)}
function policyForm(p,a){let c=p.cleanup||{},pr=p.profiles||{},sp=p.sessions||{},source=a.desired_policy&&a.desired_policy.revision?'Master-Soll-Policy':'Agent-Ist-Policy';return'