templates/index.html aktualisiert
This commit is contained in:
@@ -2,104 +2,155 @@
|
|||||||
<html lang="de">
|
<html lang="de">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Pi-Orchestrator AI</title>
|
<title>Pi-Orchestrator AI Control Center</title>
|
||||||
<script src="https://cdn.tailwindcss.com"></script>
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@5.1.0/css/xterm.css" />
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/xterm@5.1.0/lib/xterm.js"></script>
|
||||||
|
<style>
|
||||||
|
/* Custom scrollbar für den Matrix-Look */
|
||||||
|
#install-log::-webkit-scrollbar { width: 5px; }
|
||||||
|
#install-log::-webkit-scrollbar-thumb { background: #22c55e; }
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body class="bg-gray-900 text-white flex h-screen">
|
<body class="bg-gray-900 text-white h-screen flex flex-col overflow-hidden">
|
||||||
|
|
||||||
<div class="w-1/4 border-r border-gray-700 p-4">
|
|
||||||
<h2 class="text-xl font-bold mb-4">📍 Nodes</h2>
|
|
||||||
<div id="node-list">
|
|
||||||
<button onclick="addNode()" class="w-full bg-blue-600 p-2 rounded mb-4">+ Node hinzufügen</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex-1 flex flex-col">
|
|
||||||
<div id="chat-window" class="flex-1 p-6 overflow-y-auto space-y-4">
|
|
||||||
<div class="bg-gray-800 p-3 rounded-lg w-fit">Willkommen! Wie soll ich den Cluster verwalten?</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="p-4 border-t border-gray-700 flex">
|
|
||||||
<input id="user-input" type="text" class="flex-1 bg-gray-800 p-3 rounded-l outline-none" placeholder="Befehl eingeben (z.B. 'Installiere Docker auf allen Nodes')">
|
|
||||||
<button onclick="sendMessage()" class="bg-green-600 px-6 rounded-r">Senden</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@5.1.0/css/xterm.css" />
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/xterm@5.1.0/lib/xterm.js"></script>
|
|
||||||
|
|
||||||
<div class="flex flex-col h-full">
|
|
||||||
<div class="flex flex-1 overflow-hidden">
|
<div class="flex flex-1 overflow-hidden">
|
||||||
|
|
||||||
|
<div class="w-64 border-r border-gray-700 p-4 flex flex-col bg-gray-800">
|
||||||
|
<h2 class="text-xl font-bold mb-4 flex items-center">
|
||||||
|
<span class="mr-2">📍</span> Nodes
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div id="node-list" class="flex-1 overflow-y-auto space-y-2">
|
||||||
|
{% for node in nodes %}
|
||||||
|
<div class="p-3 bg-gray-700 rounded-lg border border-gray-600 hover:border-blue-500 transition-colors">
|
||||||
|
<div class="font-bold text-blue-400">{{ node.name }}</div>
|
||||||
|
<div class="text-xs text-gray-400">{{ node.ip }}</div>
|
||||||
|
<div class="text-[10px] uppercase mt-1 text-green-500 font-mono">{{ node.status }}</div>
|
||||||
|
<button onclick="openTerminal('{{ node.ip }}')" class="mt-2 text-[10px] bg-gray-600 hover:bg-gray-500 px-2 py-1 rounded w-full">Terminal öffnen</button>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button onclick="addNode()" class="mt-4 w-full bg-blue-600 hover:bg-blue-700 p-2 rounded font-bold transition-colors">
|
||||||
|
+ Node hinzufügen
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="h-1/3 border-t border-gray-600 bg-black flex">
|
<div class="flex-1 flex flex-col bg-gray-900">
|
||||||
<div id="install-log" class="w-1/2 p-2 text-xs font-mono overflow-y-auto text-green-400 border-r border-gray-700">
|
<div id="chat-window" class="flex-1 p-6 overflow-y-auto space-y-4">
|
||||||
> System bereit. Warte auf Befehle...
|
<div class="bg-gray-800 p-3 rounded-lg w-fit border border-gray-700">
|
||||||
|
<span class="text-blue-400 font-bold">Bot:</span> Willkommen im Cluster-Management. Ich bin bereit für deine Befehle.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="p-4 border-t border-gray-700 bg-gray-800 flex gap-2">
|
||||||
|
<input id="user-input" type="text"
|
||||||
|
class="flex-1 bg-gray-700 p-3 rounded-lg outline-none border border-gray-600 focus:border-blue-500"
|
||||||
|
placeholder="Frag die KI (z.B. 'Werfe Nginx auf Pi-1 ab')">
|
||||||
|
<button onclick="sendMessage()" class="bg-green-600 hover:bg-green-700 px-6 rounded-lg font-bold transition-colors">
|
||||||
|
Senden
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="h-1/3 border-t-2 border-blue-900 bg-black flex">
|
||||||
|
<div class="w-1/2 flex flex-col border-r border-gray-800">
|
||||||
|
<div class="bg-gray-800 px-3 py-1 text-[10px] font-bold text-gray-400 uppercase tracking-widest">Setup Logs</div>
|
||||||
|
<div id="install-log" class="flex-1 p-3 text-xs font-mono overflow-y-auto text-green-500">
|
||||||
|
<div>> System bereit. Warte auf Node-Aktionen...</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="terminal" class="w-1/2"></div>
|
<div class="w-1/2 flex flex-col">
|
||||||
|
<div class="bg-gray-800 px-3 py-1 text-[10px] font-bold text-gray-400 uppercase tracking-widest">Active Terminal</div>
|
||||||
|
<div id="terminal" class="flex-1 p-1"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// Xterm.js Initialisierung
|
// --- Xterm.js Setup ---
|
||||||
const term = new Terminal({ theme: { background: '#000' }, cursorBlink: true });
|
const term = new Terminal({
|
||||||
|
theme: { background: '#000000', foreground: '#ffffff' },
|
||||||
|
cursorBlink: true,
|
||||||
|
fontSize: 12,
|
||||||
|
fontFamily: 'Courier New'
|
||||||
|
});
|
||||||
term.open(document.getElementById('terminal'));
|
term.open(document.getElementById('terminal'));
|
||||||
term.write('Verfügbare Terminals: Wähle einen Node aus...\r\n');
|
term.write('System initialisiert. Terminal bereit.\r\n');
|
||||||
|
|
||||||
// WebSocket für Installation-Logs
|
// --- WebSocket Chat ---
|
||||||
const logWs = new WebSocket(`ws://${location.host}/ws/install_logs`);
|
|
||||||
logWs.onmessage = (event) => {
|
|
||||||
const logDiv = document.getElementById('install-log');
|
|
||||||
logDiv.innerHTML += `<div>${event.data}</div>`;
|
|
||||||
logDiv.scrollTop = logDiv.scrollHeight;
|
|
||||||
};
|
|
||||||
|
|
||||||
function startInstallation(ip, user, pass) {
|
|
||||||
// Diese Funktion wird gerufen, wenn ein neuer Node hinzugefügt wird
|
|
||||||
fetch('/api/setup_node', {
|
|
||||||
method: 'POST',
|
|
||||||
body: JSON.stringify({ip, user, pass})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
const ws = new WebSocket(`ws://${location.host}/ws/chat`);
|
const ws = new WebSocket(`ws://${location.host}/ws/chat`);
|
||||||
const chatWindow = document.getElementById('chat-window');
|
const chatWindow = document.getElementById('chat-window');
|
||||||
|
|
||||||
ws.onmessage = (event) => {
|
ws.onmessage = (event) => {
|
||||||
const msg = document.createElement('div');
|
appendMessage('Bot', event.data, 'bg-blue-900 border-blue-700');
|
||||||
msg.className = "bg-blue-900 p-3 rounded-lg w-fit self-start";
|
|
||||||
msg.innerText = event.data;
|
|
||||||
chatWindow.appendChild(msg);
|
|
||||||
chatWindow.scrollTop = chatWindow.scrollHeight;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function sendMessage() {
|
function sendMessage() {
|
||||||
const input = document.getElementById('user-input');
|
const input = document.getElementById('user-input');
|
||||||
|
if (!input.value) return;
|
||||||
ws.send(input.value);
|
ws.send(input.value);
|
||||||
|
appendMessage('Du', input.value, 'bg-gray-700 border-gray-600 self-end ml-auto');
|
||||||
const userMsg = document.createElement('div');
|
|
||||||
userMsg.className = "bg-gray-700 p-3 rounded-lg w-fit self-end ml-auto";
|
|
||||||
userMsg.innerText = input.value;
|
|
||||||
chatWindow.appendChild(userMsg);
|
|
||||||
|
|
||||||
input.value = '';
|
input.value = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function appendMessage(sender, text, classes) {
|
||||||
|
const msg = document.createElement('div');
|
||||||
|
msg.className = `${classes} p-3 rounded-lg w-fit max-w-[80%] border`;
|
||||||
|
msg.innerHTML = `<span class="font-bold">${sender}:</span> ${text}`;
|
||||||
|
chatWindow.appendChild(msg);
|
||||||
|
chatWindow.scrollTop = chatWindow.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- WebSocket Installation Logs ---
|
||||||
|
const logWs = new WebSocket(`ws://${location.host}/ws/install_logs`);
|
||||||
|
logWs.onmessage = (event) => {
|
||||||
|
const logDiv = document.getElementById('install-log');
|
||||||
|
const entry = document.createElement('div');
|
||||||
|
entry.className = "mb-1";
|
||||||
|
entry.innerText = `> ${event.data}`;
|
||||||
|
logDiv.appendChild(entry);
|
||||||
|
logDiv.scrollTop = logDiv.scrollHeight;
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- Node Funktionen ---
|
||||||
async function addNode() {
|
async function addNode() {
|
||||||
const ip = prompt("IP des neuen Pi:");
|
const name = prompt("Name des Pi (z.B. Pi-Server-1):");
|
||||||
const name = prompt("Name des Pi:");
|
const ip = prompt("IP Adresse:");
|
||||||
if(ip && name) {
|
const user = prompt("SSH Nutzername (meist 'pi' oder dein Name):", "pi");
|
||||||
|
const password = prompt("Initiales SSH Passwort (wird nur einmalig für Key-Copy benötigt):");
|
||||||
|
|
||||||
|
if (name && ip && user && password) {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('name', name);
|
||||||
|
formData.append('ip', ip);
|
||||||
|
formData.append('user', user);
|
||||||
|
formData.append('password', password);
|
||||||
|
|
||||||
await fetch('/add_node', {
|
await fetch('/add_node', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {'Content-Type': 'application/json'},
|
body: formData
|
||||||
body: JSON.stringify({ip, name})
|
|
||||||
});
|
});
|
||||||
location.reload();
|
location.reload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openTerminal(ip) {
|
||||||
|
term.clear();
|
||||||
|
term.write(`Verbinde mit ${ip}...\r\n`);
|
||||||
|
// Hier würde die WebSocket Verbindung für das spezifische Terminal initiiert werden
|
||||||
|
const termWs = new WebSocket(`ws://${location.host}/ws/terminal/${ip}`);
|
||||||
|
termWs.onmessage = (event) => {
|
||||||
|
term.write(event.data.replace(/\n/g, '\r\n'));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enter-Taste zum Senden
|
||||||
|
document.getElementById('user-input').addEventListener('keypress', function (e) {
|
||||||
|
if (e.key === 'Enter') sendMessage();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
Reference in New Issue
Block a user