freeapi / templates /dashboard.html
xtom0's picture
Simplify to single-model chat app: llama3.1:8b only. Add /api/chat SSE streaming, chatbot UI, remove model selection/download UI, build-time model pull
946d512
Raw
History Blame Contribute Delete
6.17 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dashboard - Ollama</title>
<link rel="stylesheet" href="/static/style.css">
</head>
<body>
<div class="container">
<header>
<h1>Dashboard</h1>
<div>
<span id="ollama-badge" class="status status-pending">Checking...</span>
<a href="/chat" class="btn btn-primary">Chat</a>
<button class="btn btn-secondary" onclick="logout()">Logout</button>
</div>
</header>
<div class="dashboard-grid">
<div class="card">
<h2>Your Profile</h2>
<div id="profile"></div>
</div>
<div class="card">
<h2>API Keys</h2>
<form onsubmit="createApiKey(event)">
<input type="text" id="key-name" placeholder="Key name" required>
<button type="submit" class="btn btn-primary">Generate</button>
</form>
<ul id="api-keys-list" class="key-list"></ul>
</div>
<div class="card">
<h2>Model Status</h2>
<p><strong>Model:</strong> llama3.1:8b</p>
<p><strong>Status:</strong> <span id="model-status-text">Checking...</span></p>
<div id="installed-section">
<p><strong>Installed Models:</strong></p>
<ul id="installed-models" class="model-list"><li>Loading...</li></ul>
</div>
<a href="/chat" class="btn btn-primary" style="width:100%;margin-top:12px;text-align:center">Open Chat</a>
</div>
<div class="card full-width">
<h2>Quick Start</h2>
<p>Use the chat interface to interact with <strong>llama3.1:8b</strong>.</p>
<p>Generate API keys above to access the model programmatically via <code>POST /api/chat</code>.</p>
<p style="margin-top:8px;font-size:0.9rem;color:#666;">
Model status: <span id="status-sub">checking...</span>
</p>
</div>
</div>
</div>
<script>
const token = localStorage.getItem('token');
if (!token) window.location.href = '/login';
async function api(url, opts = {}) {
opts.headers = {...opts.headers, 'Authorization': `Bearer ${token}`};
if (opts.body && typeof opts.body === 'object') {
opts.headers['Content-Type'] = 'application/json';
opts.body = JSON.stringify(opts.body);
}
const res = await fetch(url, opts);
if (res.status === 401) { localStorage.removeItem('token'); window.location.href = '/login'; }
return res;
}
function logout() { localStorage.removeItem('token'); window.location.href = '/login'; }
// Load profile
api('/api/me').then(r => r.json()).then(data => {
document.getElementById('profile').innerHTML = `
<p><strong>Username:</strong> ${data.username}</p>
<p><strong>Email:</strong> ${data.email}</p>
<p><strong>Default API Key:</strong> <code>${data.api_key}</code></p>
`;
});
// Check status
async function loadStatus() {
const res = await fetch('/api/status', {headers: {'Authorization': `Bearer ${token}`}});
const data = await res.json();
const badge = document.getElementById('ollama-badge');
const statusText = document.getElementById('model-status-text');
const subText = document.getElementById('status-sub');
if (data.model) {
badge.className = 'status status-completed';
badge.textContent = 'Ready';
statusText.textContent = 'Ready';
subText.textContent = 'llama3.1:8b is ready';
} else if (data.ollama) {
badge.className = 'status status-downloading';
badge.textContent = 'Downloading...';
statusText.textContent = 'Downloading model...';
subText.textContent = data.message || 'Model is being downloaded';
setTimeout(loadStatus, 10000);
} else {
badge.className = 'status status-error';
badge.textContent = 'Unavailable';
statusText.textContent = 'Unavailable';
subText.textContent = data.message || 'Ollama not reachable';
}
// Installed models
const ul = document.getElementById('installed-models');
if (data.installed && data.installed.length > 0) {
ul.innerHTML = data.installed.map(m => `<li>${m}</li>`).join('');
} else {
ul.innerHTML = '<li>None yet</li>';
}
}
loadStatus();
// Load API keys
async function loadApiKeys() {
const res = await api('/api/api-keys');
const data = await res.json();
const ul = document.getElementById('api-keys-list');
ul.innerHTML = data.api_keys.map(k => `
<li>
<strong>${k.key_name}</strong>: <code>${k.api_key}</code>
<button onclick="revokeKey(${k.id})" class="btn-small btn-danger">Revoke</button>
</li>
`).join('');
}
loadApiKeys();
async function createApiKey(e) {
e.preventDefault();
const name = document.getElementById('key-name').value;
await api('/api/api-keys', {method: 'POST', body: {key_name: name}});
document.getElementById('key-name').value = '';
loadApiKeys();
}
async function revokeKey(id) {
if (confirm('Revoke this API key?')) {
await api(`/api/api-keys/${id}`, {method: 'DELETE'});
loadApiKeys();
}
}
</script>
</body>
</html>