Fix NeuralNet empty API responses
mindflux-ci / build (push) Successful in 23s

This commit is contained in:
AliceDarkest
2026-07-14 16:13:17 +02:00
parent 85e8647cdd
commit fb487be037
4 changed files with 104 additions and 2 deletions
+99 -1
View File
@@ -1 +1,99 @@
(function(){'use strict';var selected=null,me='',poll=null;function q(s){return document.querySelector(s)}function esc(s){return String(s).replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]))}async function api(url,method,body){var r=await fetch('/api/neuralnet'+url,{method:method||'GET',headers:{'Content-Type':'application/json'},body:body?JSON.stringify(body):undefined});if(!r.ok){var e=await r.json().catch(()=>({message:'Anfrage fehlgeschlagen.'}));throw new Error(e.message)}return r.status===204?null:r.json()}function item(name,actions){return '<div class="nn-item"><span>'+esc(name)+'</span><span class="nn-actions">'+actions+'</span></div>'}function render(data){q('[data-nn-incoming]').innerHTML=data.incoming.length?data.incoming.map(n=>item(n,'<button data-action="accept" data-user="'+esc(n)+'">Annehmen</button><button data-action="decline" data-user="'+esc(n)+'">Ablehnen</button>')).join(''):'<span class="mf-small-hint">Keine offenen Anfragen.</span>';q('[data-nn-outgoing]').innerHTML=data.outgoing.length?data.outgoing.map(n=>item(n,'<button data-action="cancel" data-user="'+esc(n)+'">Zurückziehen</button>')).join(''):'<span class="mf-small-hint">Keine gesendeten Anfragen.</span>';q('[data-nn-connections]').innerHTML=data.connections.length?data.connections.map(n=>item(n,'<button data-action="chat" data-user="'+esc(n)+'" class="'+(selected===n?'active':'')+'">Chat</button>')).join(''):'<span class="mf-small-hint">Noch keine Verbindungen.</span>'}async function refresh(){var data=await api('/overview');me=data.username;q('#nnOptIn').hidden=data.enabled;q('#nnApp').hidden=!data.enabled;if(data.enabled)render(data)}async function loadChat(){if(!selected)return;var rows=await api('/messages/'+encodeURIComponent(selected));q('[data-nn-empty]').hidden=true;q('[data-nn-conversation]').hidden=false;q('[data-nn-partner]').textContent=selected;q('[data-nn-messages]').innerHTML=rows.map(m=>'<article class="nn-message '+(m.sender===me?'mine':'')+'">'+esc(m.body)+'<small>'+esc(m.sender)+' · '+new Date(m.sentAt).toLocaleString('de-DE',{dateStyle:'short',timeStyle:'short'})+'</small></article>').join('');var box=q('[data-nn-messages]');box.scrollTop=box.scrollHeight}function message(text){var old=q('.nn-head p');old.textContent=text;setTimeout(()=>old.textContent='Erst eine bestätigte Anfrage, dann ein geschützter Nachrichtenkanal.',3500)}function init(){q('[data-nn-enable]').onclick=async()=>{await api('/enable','POST');refresh()};q('[data-nn-request]').onsubmit=async e=>{e.preventDefault();try{await api('/requests','POST',{username:q('#nn-user').value});q('#nn-user').value='';message('Kontaktanfrage gesendet.')}catch(x){message(x.message)}refresh()};q('.nn-sidebar').onclick=async e=>{var b=e.target.closest('[data-action]');if(!b)return;var u=b.dataset.user,a=b.dataset.action;try{if(a==='chat'){selected=u;await refresh();await loadChat();return}if(a==='accept')await api('/requests/'+encodeURIComponent(u)+'/accept','POST');if(a==='decline')await api('/requests/'+encodeURIComponent(u)+'/decline','POST');if(a==='cancel')await api('/requests/'+encodeURIComponent(u),'DELETE');await refresh()}catch(x){message(x.message)}};q('[data-nn-compose]').onsubmit=async e=>{e.preventDefault();try{await api('/messages','POST',{recipient:selected,body:q('#nn-message').value});q('#nn-message').value='';loadChat()}catch(x){message(x.message)}};refresh().catch(x=>message(x.message));poll=setInterval(()=>{refresh();if(selected)loadChat()},5000)}if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',init);else init()})();
(function () {
'use strict';
var selected = null, me = '', poll = null;
function q(selector) { return document.querySelector(selector); }
function esc(value) {
return String(value).replace(/[&<>"']/g, function (character) {
return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[character];
});
}
async function api(url, method, body) {
var response = await fetch('/api/neuralnet' + url, {
method: method || 'GET',
headers: { 'Content-Type': 'application/json' },
body: body ? JSON.stringify(body) : undefined
});
var text = await response.text(), data = null;
if (text) {
try { data = JSON.parse(text); } catch (ignored) { data = null; }
}
if (!response.ok) throw new Error((data && data.message) || 'Anfrage fehlgeschlagen.');
return data;
}
function item(name, actions) {
return '<div class="nn-item"><span>' + esc(name) + '</span><span class="nn-actions">' + actions + '</span></div>';
}
function render(data) {
q('[data-nn-incoming]').innerHTML = data.incoming.length ? data.incoming.map(function (name) {
return item(name, '<button data-action="accept" data-user="' + esc(name) + '">Annehmen</button><button data-action="decline" data-user="' + esc(name) + '">Ablehnen</button>');
}).join('') : '<span class="mf-small-hint">Keine offenen Anfragen.</span>';
q('[data-nn-outgoing]').innerHTML = data.outgoing.length ? data.outgoing.map(function (name) {
return item(name, '<button data-action="cancel" data-user="' + esc(name) + '">Zurückziehen</button>');
}).join('') : '<span class="mf-small-hint">Keine gesendeten Anfragen.</span>';
q('[data-nn-connections]').innerHTML = data.connections.length ? data.connections.map(function (name) {
return item(name, '<button data-action="chat" data-user="' + esc(name) + '" class="' + (selected === name ? 'active' : '') + '">Chat</button>');
}).join('') : '<span class="mf-small-hint">Noch keine Verbindungen.</span>';
}
async function refresh() {
var data = await api('/overview');
me = data.username;
q('#nnOptIn').hidden = data.enabled;
q('#nnApp').hidden = !data.enabled;
if (data.enabled) render(data);
}
async function loadChat() {
if (!selected) return;
var rows = await api('/messages/' + encodeURIComponent(selected));
q('[data-nn-empty]').hidden = true;
q('[data-nn-conversation]').hidden = false;
q('[data-nn-partner]').textContent = selected;
q('[data-nn-messages]').innerHTML = rows.map(function (message) {
return '<article class="nn-message ' + (message.sender === me ? 'mine' : '') + '">' + esc(message.body) + '<small>' + esc(message.sender) + ' · ' + new Date(message.sentAt).toLocaleString('de-DE', { dateStyle: 'short', timeStyle: 'short' }) + '</small></article>';
}).join('');
var box = q('[data-nn-messages]');
box.scrollTop = box.scrollHeight;
}
function message(text) {
var hint = q('.nn-head p');
hint.textContent = text;
setTimeout(function () { hint.textContent = 'Erst eine bestätigte Anfrage, dann ein geschützter Nachrichtenkanal.'; }, 3500);
}
function init() {
q('[data-nn-enable]').onclick = async function () { await api('/enable', 'POST'); refresh(); };
q('[data-nn-request]').onsubmit = async function (event) {
event.preventDefault();
try {
await api('/requests', 'POST', { username: q('#nn-user').value });
q('#nn-user').value = '';
message('Kontaktanfrage gesendet.');
} catch (error) { message(error.message); }
refresh();
};
q('.nn-sidebar').onclick = async function (event) {
var button = event.target.closest('[data-action]');
if (!button) return;
var user = button.dataset.user, action = button.dataset.action;
try {
if (action === 'chat') { selected = user; await refresh(); await loadChat(); return; }
if (action === 'accept') await api('/requests/' + encodeURIComponent(user) + '/accept', 'POST');
if (action === 'decline') await api('/requests/' + encodeURIComponent(user) + '/decline', 'POST');
if (action === 'cancel') await api('/requests/' + encodeURIComponent(user), 'DELETE');
await refresh();
} catch (error) { message(error.message); }
};
q('[data-nn-compose]').onsubmit = async function (event) {
event.preventDefault();
try {
await api('/messages', 'POST', { recipient: selected, body: q('#nn-message').value });
q('#nn-message').value = '';
loadChat();
} catch (error) { message(error.message); }
};
refresh().catch(function (error) { message(error.message); });
poll = setInterval(function () { refresh(); if (selected) loadChat(); }, 5000);
}
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init); else init();
})();
+1 -1
View File
@@ -4,7 +4,7 @@
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NeuralNet – SysnaptiK Neural</title>
<link rel="stylesheet" href="/css/theme.css?v=3"><link rel="stylesheet" href="/css/neuralnet.css?v=1">
<script src="/js/theme.js?v=3"></script><script src="/js/neuralnet.js?v=1" defer></script>
<script src="/js/theme.js?v=3"></script><script src="/js/neuralnet.js?v=2" defer></script>
</head>
<body>
<div class="mf-theme-float"><button class="mf-theme"></button></div>