Files

261 lines
28 KiB
HTML

<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DTNum Labs</title>
<style>
* { margin:0; padding:0; box-sizing:border-box; }
body { font-family: 'Segoe UI', sans-serif; background:#0f172a; color:#e2e8f0; min-height:100vh; }
header { background:#1e293b; padding:16px 32px; display:flex; justify-content:space-between; align-items:center; border-bottom:1px solid #334155; }
header h1 { font-size:20px; color:#60a5fa; font-weight:700; }
.user-info { display:flex; align-items:center; gap:12px; font-size:14px; color:#94a3b8; }
.logout { color:#f87171; text-decoration:none; font-size:13px; }
.container { max-width:1100px; margin:0 auto; padding:32px 24px; }
h2 { font-size:16px; color:#94a3b8; text-transform:uppercase; letter-spacing:.1em; margin-bottom:16px; }
.grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(300px,1fr)); gap:16px; margin-bottom:40px; }
.card { background:#1e293b; border:1px solid #334155; border-radius:12px; padding:20px; }
.card-icon { font-size:32px; margin-bottom:12px; }
.card-name { font-size:16px; font-weight:700; margin-bottom:6px; }
.card-status { font-size:12px; color:#94a3b8; margin-bottom:16px; }
.card-status.running { color:#34d399; }
.btn { padding:8px 16px; border-radius:8px; border:none; cursor:pointer; font-size:13px; font-weight:600; transition:all .2s; }
.btn-launch { background:#3b82f6; color:#fff; }
.btn-launch:hover { background:#2563eb; }
.btn-open { background:#10b981; color:#fff; }
.btn-open:hover { background:#059669; }
.btn-delete { background:#ef4444; color:#fff; }
.btn-delete:hover { background:#dc2626; }
.btn-group { display:flex; gap:8px; flex-wrap:wrap; }
.badge { display:inline-block; padding:2px 8px; border-radius:999px; font-size:11px; font-weight:600; }
.badge-running { background:#064e3b; color:#34d399; }
.badge-stopped { background:#1e293b; color:#64748b; border:1px solid #334155; }
.minio-info { background:#1e293b; border:1px solid #334155; border-radius:10px; padding:16px 20px; margin-top:8px; font-size:13px; color:#94a3b8; }
.minio-info strong { color:#60a5fa; }
.token-box { display:flex; align-items:center; gap:8px; background:#0f172a; border:1px solid #334155; border-radius:6px; padding:6px 10px; margin-top:10px; font-family:monospace; font-size:12px; color:#94a3b8; }
.token-box span { flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.btn-copy { background:#334155; color:#94a3b8; border:none; border-radius:6px; padding:4px 8px; font-size:11px; cursor:pointer; flex-shrink:0; transition:all .2s; }
.btn-copy:hover { background:#475569; color:#e2e8f0; }
.btn-copy.copied { background:#064e3b; color:#34d399; }
</style>
</head>
<body>
<header>
<div style="display:flex;align-items:center;gap:10px;"><img src="data:image/png;base64,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" style="height:36px;filter:invert(1);"/><span style="font-size:18px;font-weight:700;color:#60a5fa;">DTNum Labs</span></div>
<div class="user-info">
👤 {{ username }}
<a href="/logout" class="logout">Déconnexion</a>
</div>
</header>
<div class="container">
<!-- Services actifs -->
<h2>Mes services actifs</h2>
<div class="grid" id="active-services">
{% if services %}
{% for svc in services %}
<div class="card">
<div class="card-icon">{{ catalog[svc.type].icon if svc.type in catalog else '🔧' }}</div>
<div class="card-name">{{ catalog[svc.type].name if svc.type in catalog else svc.type }}</div>
<div class="card-status">
<span class="badge {% if svc.ready %}badge-running{% else %}badge-stopped{% endif %}">
{% if svc.ready %}● En cours{% else %}⏳ Démarrage...{% endif %}
</span>
</div>
{% if svc.token %}<div class="token-box"><span>{{ svc.token }}</span><button class="btn-copy" onclick="copyToken('{{ svc.token }}', this)">📋 Copier</button></div>{% endif %}<div class="btn-group">
{% if svc.ready %}
<a href="{{ svc.url }}" target="_blank"><button class="btn btn-open">🔗 Ouvrir</button></a>
{% endif %}
<button class="btn btn-delete" onclick="deleteService('{{ svc.name }}')">🗑 Supprimer</button>
</div>
</div>
{% endfor %}
{% else %}
<div style="color:#64748b; font-size:14px; padding:16px 0;">Aucun service actif. Lance un service depuis le catalogue.</div>
{% endif %}
</div>
<!-- Catalogue -->
<h2>Catalogue de services</h2>
<div class="grid">
{% for key, svc in catalog.items() %}
<div class="card">
<div class="card-icon">{{ svc.icon }}</div>
<div class="card-name">{{ svc.name }}</div>
<div class="card-status">Port {{ svc.port }}</div>
<button class="btn btn-launch" onclick="launchService('{{ key }}')">▶ Lancer</button>
</div>
{% endfor %}
</div>
<!-- MinIO info -->
<div class="minio-info" id="minio-block">
🪣 Ton stockage S3 : <strong>s3://user-{{ username }}/</strong>
<a href="#" onclick="showMinioCredentials(event)" style="color:#60a5fa;">Voir les accès MinIO</a>
<div id="minio-creds" style="display:none; margin-top:12px;">
<div style="margin-bottom:6px;">
🔗 <a id="minio-url" href="#" target="_blank" style="color:#34d399;"></a>
</div>
<div style="margin-bottom:4px;">👤 Login : <span id="minio-login" style="color:#e2e8f0; font-family:monospace;"></span>
<button class="btn-copy" onclick="copyText(document.getElementById('minio-login').textContent, this)">📋</button>
</div>
<div>🔑 Password : <span id="minio-password" style="color:#e2e8f0; font-family:monospace;"></span>
<button class="btn-copy" onclick="copyText(document.getElementById('minio-password').textContent, this)">📋</button>
</div>
</div>
</div>
</div>
<script>
async function launchService(type) {
const btn = event.target;
btn.disabled = true;
btn.textContent = '⏳ Lancement...';
const r = await fetch(`/api/launch/${type}`, { method: 'POST' });
const d = await r.json();
if (d.url) {
alert(`✅ Service lancé !\n\nURL : ${d.url}\n\nLe certificat TLS sera prêt dans ~2 minutes.`);
location.reload();
} else {
alert('Erreur lors du lancement');
btn.disabled = false;
btn.textContent = '▶ Lancer';
}
}
function copyToken(token, btn) {
navigator.clipboard.writeText(token).then(() => {
btn.textContent = "✅ Copié !";
btn.classList.add("copied");
setTimeout(() => { btn.textContent = "📋 Copier"; btn.classList.remove("copied"); }, 2000);
});
}
async function deleteService(type) {
if (!confirm('Supprimer ce service ?')) return;
await fetch(`/api/delete/${type}`, { method: 'DELETE' });
location.reload();
}
async function showMinioCredentials(e) {
e.preventDefault();
const div = document.getElementById("minio-creds");
if (div.style.display === "none") {
const r = await fetch("/api/minio-credentials");
const d = await r.json();
document.getElementById("minio-url").href = d.url;
document.getElementById("minio-url").textContent = d.url;
document.getElementById("minio-login").textContent = d.access_key;
document.getElementById("minio-password").textContent = d.secret_key;
div.style.display = "block";
} else {
div.style.display = "none";
}
}
function copyText(text, btn) {
navigator.clipboard.writeText(text).then(() => {
btn.textContent = "✅";
setTimeout(() => btn.textContent = "📋", 2000);
});
}
</script>
<button id="chat-btn" onclick="toggleChat()" style="position:fixed;bottom:24px;right:24px;z-index:9999;width:52px;height:52px;border-radius:50%;background:#3b82f6;color:#fff;border:none;cursor:pointer;font-size:22px;box-shadow:0 4px 16px rgba(59,130,246,0.4);">🤖</button>
<div id="chat-window" style="position:fixed;bottom:88px;right:24px;z-index:9998;width:360px;height:480px;background:#1e293b;border:1px solid #334155;border-radius:16px;display:none;flex-direction:column;box-shadow:0 8px 32px rgba(0,0,0,0.4);overflow:hidden;">
<div style="padding:14px 16px;background:#0f172a;border-bottom:1px solid #334155;display:flex;align-items:center;justify-content:space-between;">
<div style="display:flex;align-items:center;gap:8px;">
<span style="font-size:18px;">🤖</span>
<div>
<div style="font-size:14px;font-weight:600;color:#e2e8f0;">Assistant IA</div>
<div style="font-size:11px;color:#34d399;">● llama3.2:3b</div>
</div>
</div>
<button onclick="clearChat()" style="background:none;border:none;color:#64748b;cursor:pointer;font-size:18px;">🗑</button>
</div>
<div id="chat-messages" style="flex:1;overflow-y:auto;padding:12px;display:flex;flex-direction:column;gap:10px;">
<div style="background:#0f172a;border-radius:10px 10px 10px 2px;padding:10px 12px;font-size:13px;color:#94a3b8;max-width:85%;">Bonjour ! Je suis ton assistant Data Science. Pose-moi une question sur Python, pandas, SQL ou MinIO.</div>
</div>
<div style="padding:10px 12px;border-top:1px solid #334155;display:flex;gap:8px;">
<input id="chat-input" type="text" placeholder="Pose ta question..."
style="flex:1;background:#0f172a;border:1px solid #334155;border-radius:8px;color:#e2e8f0;padding:8px 12px;font-size:13px;outline:none;"/>
<button id="send-btn" onclick="sendMessage()" style="background:#3b82f6;border:none;border-radius:8px;color:#fff;padding:8px 14px;cursor:pointer;font-size:16px;"></button>
</div>
</div>
<style>
#chat-messages::-webkit-scrollbar{width:4px;}
#chat-messages::-webkit-scrollbar-thumb{background:#334155;border-radius:2px;}
.msg-user{background:#1e40af;border-radius:10px 10px 2px 10px;padding:10px 12px;font-size:13px;color:#e2e8f0;max-width:85%;align-self:flex-end;}
.msg-ai{background:#0f172a;border-radius:10px 10px 10px 2px;padding:10px 12px;font-size:13px;color:#e2e8f0;max-width:85%;white-space:pre-wrap;line-height:1.5;}
</style>
<script>
var chatHistory = [];
var chatOpen = false;
function toggleChat() {
chatOpen = !chatOpen;
var win = document.getElementById('chat-window');
var btn = document.getElementById('chat-btn');
win.style.display = chatOpen ? 'flex' : 'none';
btn.textContent = chatOpen ? 'X' : '🤖';
if (chatOpen) document.getElementById('chat-input').focus();
}
function clearChat() {
chatHistory = [];
document.getElementById('chat-messages').innerHTML = '<div class="msg-ai">Chat effacé.</div>';
}
function appendMsg(text, type) {
var msgs = document.getElementById('chat-messages');
var div = document.createElement('div');
div.className = type === 'user' ? 'msg-user' : 'msg-ai';
div.textContent = text;
msgs.appendChild(div);
msgs.scrollTop = msgs.scrollHeight;
return div;
}
function sendMessage() {
var input = document.getElementById('chat-input');
var text = input.value.trim();
if (!text) return;
input.value = '';
appendMsg(text, 'user');
chatHistory.push({role: 'user', content: text});
var thinking = appendMsg('En train de réfléchir...', 'ai');
thinking.style.color = '#64748b';
document.getElementById('send-btn').disabled = true;
fetch('/api/chat', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({messages: chatHistory})
}).then(function(r) { return r.json(); }).then(function(d) {
thinking.remove();
var reply = d.response || 'Erreur.';
appendMsg(reply, 'ai');
chatHistory.push({role: 'assistant', content: reply});
document.getElementById('send-btn').disabled = false;
input.focus();
}).catch(function() {
thinking.textContent = "Erreur de connexion.";
thinking.style.color = '#f87171';
document.getElementById('send-btn').disabled = false;
});
}
document.getElementById('chat-input').addEventListener('keydown', function(e) {
if (e.key === 'Enter') { e.preventDefault(); sendMessage(); }
});
</script>
</body>
</html>