mirror of
https://github.com/sol1/rustguac.git
synced 2026-09-10 17:47:13 +00:00
7acbb302fa
Session history: - New session_history SQLite table tracks all sessions with user, entry, folder, hostname, duration, and recording file - Automatic cleanup via session_history_retention_days config (default 90) - 8 new unit tests for session history DB functions Reports page (poweruser+ only): - Summary cards: total sessions, hours, unique users, active now - Sortable/filterable session history table with pagination - Top Connections and Top Users leaderboards - CSV export for session history with filters Enriched recordings: - Recording .meta sidecars now include user, folder, entry name, session type - Recordings API returns enriched metadata from .meta files - Recordings page shows User, Entry, Folder columns Other: - Reports nav link on all pages, hidden for operator/viewer roles - Reports documentation in docs/reports.md - Onboarding wizard for new users (role-scoped, dismissable) - Settings menu label visibility improvement - Fixed stale credential variable test (hyphen support)
244 lines
14 KiB
HTML
244 lines
14 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>rustguac - Docs</title>
|
|
<style>
|
|
:root { --primary: #e94560; --primary-hover: #c73652; --accent: #5bc0be; --accent-hover: #4aa3a1; --bg: #1a1a2e; --surface: #16213e; --input: #0f3460; --text: #e0e0e0; --text-muted: #aaa; --border: #333; --text-dim: #666; --text-on-primary: #fff; --btn-disabled: #555; --status-pending: #f0c040; --status-active: #5bc0be; --status-completed: #888; --status-error: #e94560; --status-expired: #666; --type-ssh-bg: #1b4332; --type-ssh-fg: #52b788; --type-rdp-bg: #3d1f00; --type-rdp-fg: #f0a050; --type-vnc-bg: #2d1b4e; --type-vnc-fg: #b07ff0; --type-web-bg: #1a1a4e; --type-web-fg: #7b8ff0; --hop-bg: #1b4332; --hop-fg: #52b788; --bg-pattern: none; }
|
|
body { font-family: monospace; background-color: var(--bg); background-image: var(--bg-pattern); background-attachment: fixed; min-height: 100vh; color: var(--text); padding: 2em; font-size: 18px; }
|
|
h1 { color: var(--primary); }
|
|
nav { margin-top: 0.8em; margin-bottom: 1.5em; padding-bottom: 0.8em; border-bottom: 1px solid var(--border); font-size: 0.95em; }
|
|
nav a { margin-right: 1.5em; text-decoration: none; color: var(--accent); }
|
|
nav a:hover { text-decoration: underline; }
|
|
nav .active { color: var(--primary); font-weight: bold; }
|
|
#user-menu-wrapper { position: relative; float: right; }
|
|
#user-menu-btn { cursor: pointer; font-size: 1.3em; color: var(--text-muted); }
|
|
#user-menu-btn:hover { color: var(--text); }
|
|
#user-menu { display:none; position:absolute; right:0; top:1.8em; background:var(--surface); border:1px solid var(--border); border-radius:6px; min-width:220px; z-index:50; padding:0.4em 0; }
|
|
.um-section-label { padding:0.4em 0.9em; font-size:0.8em; color:var(--text-dim); text-transform:uppercase; letter-spacing:0.05em; }
|
|
.um-item { padding:0.5em 0.9em; cursor:pointer; display:flex; align-items:center; gap:0.6em; font-size:0.9em; }
|
|
.um-item:hover { background:var(--input); }
|
|
.um-item.active { color:var(--accent); }
|
|
.um-theme-info { display:flex; flex-direction:column; }
|
|
.um-theme-name { font-weight:bold; }
|
|
.um-theme-desc { font-size:0.8em; color:var(--text-dim); }
|
|
.um-swatch { display:inline-block; width:16px; height:16px; border-radius:50%; border:1px solid var(--border); flex-shrink:0; }
|
|
.um-divider { height:1px; background:var(--border); margin:0.3em 0; }
|
|
.um-logout { color:var(--text-muted); }
|
|
.um-logout:hover { color:var(--primary); background:var(--input); }
|
|
|
|
/* Layout */
|
|
.docs-layout { display: flex; min-height: calc(100vh - 160px); border-top: 1px solid var(--border); margin-top: 1em; }
|
|
|
|
/* Sidebar */
|
|
.sidebar {
|
|
width: 220px;
|
|
min-width: 220px;
|
|
border-right: 1px solid var(--border);
|
|
padding: 1em 0;
|
|
overflow-y: auto;
|
|
}
|
|
.sidebar a {
|
|
display: block;
|
|
padding: 0.5em 1.2em;
|
|
color: var(--text-muted);
|
|
text-decoration: none;
|
|
font-size: 0.9em;
|
|
border-left: 3px solid transparent;
|
|
}
|
|
.sidebar a:hover { color: var(--text); background: var(--surface); }
|
|
.sidebar a.active {
|
|
color: var(--primary);
|
|
border-left-color: var(--primary);
|
|
background: var(--surface);
|
|
font-weight: bold;
|
|
}
|
|
|
|
/* Content area */
|
|
.content {
|
|
flex: 1;
|
|
overflow-y: auto;
|
|
padding: 2em 2.5em;
|
|
max-width: 900px;
|
|
}
|
|
.content h1 { color: var(--primary); font-size: 1.5em; margin-bottom: 0.8em; }
|
|
.content h2 { color: var(--primary); font-size: 1.15em; margin-top: 2em; margin-bottom: 0.5em; border-bottom: 1px solid var(--border); padding-bottom: 0.3em; }
|
|
.content h3 { color: var(--accent); font-size: 1em; margin-top: 1.5em; margin-bottom: 0.4em; }
|
|
.content h4 { color: var(--accent); font-size: 0.95em; margin-top: 1.2em; margin-bottom: 0.3em; }
|
|
.content p { margin: 0.6em 0; line-height: 1.6; }
|
|
.content ul, .content ol { margin: 0.5em 0 0.5em 1.5em; line-height: 1.6; }
|
|
.content li { margin: 0.2em 0; }
|
|
.content a { color: var(--accent); }
|
|
.content a:hover { text-decoration: underline; }
|
|
.content code {
|
|
background: var(--surface);
|
|
padding: 0.15em 0.4em;
|
|
border-radius: 3px;
|
|
font-size: 0.9em;
|
|
}
|
|
.content pre {
|
|
background: var(--surface);
|
|
border: 1px solid var(--border);
|
|
padding: 1em;
|
|
border-radius: 6px;
|
|
overflow-x: auto;
|
|
line-height: 1.5;
|
|
margin: 0.8em 0;
|
|
}
|
|
.content pre code {
|
|
background: none;
|
|
padding: 0;
|
|
border-radius: 0;
|
|
font-size: 0.85em;
|
|
}
|
|
.content table { border-collapse: collapse; width: 100%; margin: 0.8em 0; }
|
|
.content th, .content td { text-align: left; padding: 0.4em 0.8em; border-bottom: 1px solid var(--border); }
|
|
.content th { color: var(--text-muted); font-size: 0.85em; }
|
|
.content blockquote {
|
|
border-left: 3px solid var(--accent);
|
|
margin: 0.8em 0;
|
|
padding: 0.4em 1em;
|
|
color: var(--text-muted);
|
|
}
|
|
.content hr { border: none; border-top: 1px solid var(--border); margin: 2em 0; }
|
|
|
|
.loading { padding: 2em; color: var(--text-muted); }
|
|
|
|
@media (max-width: 700px) {
|
|
.sidebar { width: 160px; min-width: 160px; }
|
|
.content { padding: 1em; }
|
|
}
|
|
</style>
|
|
<script>(function(){var c=localStorage.getItem('rustguac_theme_colors');if(c){try{var o=JSON.parse(c),r=document.documentElement.style;for(var k in o)r.setProperty('--'+k.replace(/_/g,'-'),o[k]);if(o.bg_pattern&&o.bg_pattern!=='none'){var s=document.createElement('style');s.id='bg-pattern-style';s.textContent='body{background-image:'+o.bg_pattern+';background-attachment:fixed}';document.head.appendChild(s)}}catch(e){}}})();</script>
|
|
</head>
|
|
<body>
|
|
<img id="site-logo" src="/logo.svg" style="max-height:40px;vertical-align:middle;margin-right:0.5em" alt=""><h1 style="display:inline;vertical-align:middle">rustguac</h1>
|
|
<nav>
|
|
<a href="/addressbook.html">Address Book</a>
|
|
<a href="/sessions.html" id="sessions-link">Sessions</a>
|
|
<a href="/recordings.html">Recordings</a>
|
|
<a href="/reports.html" id="reports-link" style="display:none">Reports</a>
|
|
<a href="/docs.html" class="active">Docs</a>
|
|
<a href="/tokens.html" id="tokens-link" style="display:none">Tokens</a>
|
|
<a href="/admin.html" id="admin-link" style="display:none">Admin</a>
|
|
<span id="user-menu-wrapper"><span id="user-menu-btn" title="Preferences">⚙</span><div id="user-menu"><div class="um-section-label">Theme</div><div id="um-theme-list"></div><div class="um-divider"></div><div class="um-item um-logout" id="logout-item">Logout</div></div></span>
|
|
</nav>
|
|
|
|
<div class="docs-layout">
|
|
<div class="sidebar" id="sidebar"></div>
|
|
<div class="content" id="content">
|
|
<div class="loading">Loading documentation...</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
var docs = [];
|
|
var sidebar = document.getElementById('sidebar');
|
|
var content = document.getElementById('content');
|
|
|
|
function showSection(slug) {
|
|
var doc = docs.find(function(d) { return d.slug === slug; });
|
|
if (!doc && docs.length) doc = docs[0];
|
|
if (!doc) return;
|
|
|
|
content.innerHTML = doc.html;
|
|
|
|
// Update sidebar active state
|
|
var links = sidebar.querySelectorAll('a');
|
|
for (var i = 0; i < links.length; i++) {
|
|
links[i].classList.toggle('active', links[i].dataset.slug === doc.slug);
|
|
}
|
|
|
|
// Update hash without triggering hashchange
|
|
if (location.hash !== '#' + doc.slug) {
|
|
history.replaceState(null, '', '#' + doc.slug);
|
|
}
|
|
|
|
content.scrollTop = 0;
|
|
}
|
|
|
|
function getSlugFromHash() {
|
|
return location.hash ? location.hash.substring(1) : '';
|
|
}
|
|
|
|
// Load docs from API
|
|
fetch('/api/docs')
|
|
.then(function(r) { return r.json(); })
|
|
.then(function(data) {
|
|
docs = data;
|
|
|
|
// Build sidebar
|
|
var html = '';
|
|
for (var i = 0; i < docs.length; i++) {
|
|
html += '<a href="#' + docs[i].slug + '" data-slug="' + docs[i].slug + '">' + docs[i].title + '</a>';
|
|
}
|
|
sidebar.innerHTML = html;
|
|
|
|
// Click handler for sidebar links
|
|
sidebar.addEventListener('click', function(e) {
|
|
var a = e.target.closest('a');
|
|
if (!a) return;
|
|
e.preventDefault();
|
|
var slug = a.dataset.slug;
|
|
history.pushState(null, '', '#' + slug);
|
|
showSection(slug);
|
|
});
|
|
|
|
// Show initial section from hash or first
|
|
showSection(getSlugFromHash() || docs[0].slug);
|
|
})
|
|
.catch(function() {
|
|
content.innerHTML = '<div class="loading">Failed to load documentation.</div>';
|
|
});
|
|
|
|
// Handle browser back/forward
|
|
window.addEventListener('popstate', function() {
|
|
showSection(getSlugFromHash());
|
|
});
|
|
|
|
// Theme and auth
|
|
function applyThemeColors(colors){var r=document.documentElement.style;for(var k in colors)r.setProperty('--'+k.replace(/_/g,'-'),colors[k]);var s=document.getElementById('bg-pattern-style');if(!s){s=document.createElement('style');s.id='bg-pattern-style';document.head.appendChild(s)}s.textContent=colors.bg_pattern&&colors.bg_pattern!=='none'?'body{background-image:'+colors.bg_pattern+';background-attachment:fixed}':'';localStorage.setItem('rustguac_theme_colors',JSON.stringify(colors))}
|
|
var _themePresets={},_adminPreset='dark';
|
|
var _themeDescriptions={dark:'Navy & cyan \u2014 the default',light:'Clean white & blue','high-contrast':'Maximum readability',terminal:'Retro green-on-black',nord:'Arctic, muted blues',corporate:'Slate & steel blue',aurora:'Midnight blue with ambient glow',jaguar:'Racing green & gold'};
|
|
function initTheme(t){if(!t)return;_themePresets=t.presets||{};_adminPreset=t.admin_preset||'dark';var u=localStorage.getItem('rustguac_theme'),active=u&&_themePresets[u]?u:_adminPreset,colors=(active===_adminPreset)?t.admin_colors:_themePresets[active];if(colors)applyThemeColors(colors);if(t.logo_url){var l=document.getElementById('site-logo');if(l){if(l.src!==t.logo_url&&!l.src.endsWith(t.logo_url))l.src=t.logo_url;l.style.display=''}}var menu=document.getElementById('um-theme-list');if(menu){menu.innerHTML='';Object.keys(_themePresets).forEach(function(name){var item=document.createElement('div');item.className='um-item'+(name===active?' active':'');var sw=document.createElement('span');sw.className='um-swatch';var p=_themePresets[name];sw.style.background='linear-gradient(135deg,'+p.primary+' 50%,'+p.accent+' 50%)';item.appendChild(sw);var info=document.createElement('div');info.className='um-theme-info';var nm=document.createElement('span');nm.className='um-theme-name';nm.textContent=name;info.appendChild(nm);var desc=document.createElement('span');desc.className='um-theme-desc';desc.textContent=_themeDescriptions[name]||'';info.appendChild(desc);item.appendChild(info);item.addEventListener('click',function(){localStorage.setItem('rustguac_theme',name);applyThemeColors(_themePresets[name]);menu.querySelectorAll('.um-item').forEach(function(el){el.classList.remove('active')});item.classList.add('active');document.getElementById('user-menu').style.display='none'});menu.appendChild(item)})}}
|
|
var _ub=document.getElementById('user-menu-btn');if(_ub)_ub.addEventListener('click',function(e){e.stopPropagation();var m=document.getElementById('user-menu');m.style.display=m.style.display==='block'?'none':'block'});document.addEventListener('click',function(){var m=document.getElementById('user-menu');if(m)m.style.display='none'});
|
|
fetch('/api/auth/status').then(function(r){return r.json()}).then(function(d){
|
|
if(d.site_title){document.title=d.site_title+' - Docs';document.querySelector('h1').textContent=d.site_title;}
|
|
initTheme(d.theme);
|
|
});
|
|
var apiKey = sessionStorage.getItem('rustguac_api_key');
|
|
var roleLevel = { admin: 4, poweruser: 3, operator: 2, viewer: 1 };
|
|
if (apiKey) {
|
|
document.getElementById('admin-link').style.display = '';
|
|
document.getElementById('tokens-link').style.display = '';
|
|
document.getElementById('reports-link').style.display = '';
|
|
} else {
|
|
fetch('/api/me', { credentials: 'same-origin' })
|
|
.then(function(res) {
|
|
if (res.status === 401 || res.status === 403) { window.location.href = '/'; return; }
|
|
if (!res.ok) return;
|
|
return res.json();
|
|
})
|
|
.then(function(data) {
|
|
if (!data) return;
|
|
if (data.role === 'admin') document.getElementById('admin-link').style.display = '';
|
|
var level = roleLevel[data.role] || 0;
|
|
if (level >= 2) document.getElementById('tokens-link').style.display = '';
|
|
if (level >= 3) {
|
|
document.getElementById('reports-link').style.display = '';
|
|
}
|
|
if (level < 3) {
|
|
document.getElementById('sessions-link').style.display = 'none';
|
|
}
|
|
})
|
|
.catch(function() {});
|
|
}
|
|
document.getElementById('logout-item').addEventListener('click', function() {
|
|
sessionStorage.removeItem('rustguac_api_key');
|
|
fetch('/auth/logout', { credentials: 'same-origin' })
|
|
.finally(function() { window.location.href = '/'; });
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|