2025-04-11 22:45:43 -05:00

304 lines
11 KiB
HTML

<!DOCTYPE html>
<html lang="en" data-bs-theme="light">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Firefly Database Viewer</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
:root {
--bs-body-bg: #ffffff;
--bs-body-color: #212529;
}
[data-bs-theme="dark"] {
--bs-body-bg: #212529;
--bs-body-color: #f8f9fa;
}
body {
transition: background-color 0.3s ease, color 0.3s ease;
}
.key-card {
margin-bottom: 1rem;
transition: all 0.3s ease;
}
.key-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
.value-container {
max-height: 200px;
overflow-y: auto;
}
.type-badge {
font-size: 0.8rem;
padding: 0.3rem 0.6rem;
}
.connection-status {
position: fixed;
top: 1rem;
right: 1rem;
z-index: 1000;
}
.section-header {
margin-top: 2rem;
margin-bottom: 1rem;
padding-bottom: 0.5rem;
border-bottom: 2px solid #eee;
}
.email-format {
background-color: var(--bs-body-bg);
padding: 1rem;
border-radius: 0.25rem;
margin-bottom: 0.5rem;
}
.email-field {
margin-bottom: 0.5rem;
line-height: 1.4;
}
.email-field:last-child {
margin-bottom: 0;
}
.field-value {
padding: 0.5rem;
border-bottom: 1px solid #eee;
line-height: 1.4;
}
.field-value:last-child {
border-bottom: none;
}
.field-value strong {
color: var(--bs-body-color);
min-width: 80px;
display: inline-block;
}
[data-bs-theme="dark"] .card {
background-color: #2c3034;
border-color: #373b3e;
}
[data-bs-theme="dark"] .card-header {
background-color: #343a40;
border-bottom-color: #373b3e;
}
[data-bs-theme="dark"] .field-value {
border-bottom-color: #373b3e;
}
[data-bs-theme="dark"] .section-header {
border-bottom-color: #373b3e;
}
.theme-toggle {
position: fixed;
top: 1rem;
right: 1rem;
z-index: 1001;
}
</style>
</head>
<body>
<div class="container py-4">
<button class="btn btn-outline-secondary theme-toggle" onclick="toggleTheme()">
<span class="theme-icon">🌙</span>
</button>
<h1 class="mb-4">Firefly Database Viewer</h1>
<div class="alert alert-info mb-4">
<h4 class="alert-heading">Connection Status</h4>
<p id="connection-message">Checking connection...</p>
</div>
<div class="row">
<div class="col-12">
<button class="btn btn-primary mb-3" onclick="refreshData()">Refresh Data</button>
</div>
</div>
<div id="data-container">
<div id="strings-section" class="section">
<h2 class="section-header">String Keys</h2>
<div class="row" id="strings-container"></div>
</div>
<div id="lists-section" class="section">
<h2 class="section-header">List Keys</h2>
<div class="row" id="lists-container"></div>
</div>
<div id="hashes-section" class="section">
<h2 class="section-header">Hash Keys</h2>
<div class="row" id="hashes-container"></div>
</div>
</div>
</div>
<script>
// Add theme toggle functionality
function toggleTheme() {
const html = document.documentElement;
const currentTheme = html.getAttribute('data-bs-theme');
const newTheme = currentTheme === 'light' ? 'dark' : 'light';
html.setAttribute('data-bs-theme', newTheme);
// Update theme icon
const themeIcon = document.querySelector('.theme-icon');
themeIcon.textContent = newTheme === 'light' ? '🌙' : '☀️';
// Save preference
localStorage.setItem('theme', newTheme);
}
// Load saved theme preference
document.addEventListener('DOMContentLoaded', () => {
const savedTheme = localStorage.getItem('theme') || 'light';
document.documentElement.setAttribute('data-bs-theme', savedTheme);
const themeIcon = document.querySelector('.theme-icon');
themeIcon.textContent = savedTheme === 'light' ? '🌙' : '☀️';
});
function formatValue(value) {
if (Array.isArray(value)) {
// Display list values with latest values at the top
return value.map(v => typeof v === 'string' ? v.trim() : v).join('<br>');
} else if (typeof value === 'object' && value !== null) {
// Format as key-value pairs
return Object.entries(value)
.map(([key, val]) => {
let displayVal = typeof val === 'string' ? val.trim() : val;
// Special handling for email fields
if (['from', 'to', 'subject', 'content'].includes(key.toLowerCase())) {
return `<div class="field-value">
<strong>${key.charAt(0).toUpperCase() + key.slice(1)}:</strong>
${displayVal}
</div>`;
}
// Regular field formatting
return `<div class="field-value">
<strong>${key}:</strong> ${displayVal}
</div>`;
})
.join('');
}
return String(value).trim();
}
function getTypeBadgeClass(type) {
const classes = {
'string': 'bg-primary',
'list': 'bg-success',
'hash': 'bg-info'
};
return classes[type] || 'bg-secondary';
}
function updateConnectionStatus(connected, message) {
const alertDiv = document.querySelector('.alert');
const messageElement = document.getElementById('connection-message');
if (connected) {
alertDiv.className = 'alert alert-success mb-4';
messageElement.textContent = 'Connected to Firefly database successfully!';
} else {
alertDiv.className = 'alert alert-danger mb-4';
messageElement.textContent = `Connection failed: ${message}`;
}
}
function createKeyCard(key, type, value) {
const card = document.createElement('div');
card.className = 'col-md-6 col-lg-4';
card.innerHTML = `
<div class="card key-card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="card-title mb-0">${key}</h5>
<span class="badge ${getTypeBadgeClass(type)} type-badge">${type}</span>
</div>
<div class="card-body">
<div class="value-container">
${formatValue(value)}
</div>
</div>
</div>
`;
return card;
}
function refreshData() {
console.log('Fetching data from /api/keys...');
fetch('/api/keys')
.then(response => {
console.log('Received response:', response.status);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then(data => {
console.log('Parsed data:', data);
updateConnectionStatus(data.connection_status, data.error || '');
if (data.success) {
// Clear existing containers
document.getElementById('strings-container').innerHTML = '';
document.getElementById('lists-container').innerHTML = '';
document.getElementById('hashes-container').innerHTML = '';
const { strings = [], lists = [], hashes = [] } = data.data || {};
// Update strings
if (strings.length > 0) {
console.log(`Found ${strings.length} string keys`);
strings.forEach(item => {
document.getElementById('strings-container')
.appendChild(createKeyCard(item.key, 'string', item.value));
});
} else {
document.getElementById('strings-container').innerHTML =
'<div class="col-12"><div class="alert alert-info">No string keys found.</div></div>';
}
// Update lists
if (lists.length > 0) {
console.log(`Found ${lists.length} list keys`);
lists.forEach(item => {
document.getElementById('lists-container')
.appendChild(createKeyCard(item.key, 'list', item.value));
});
} else {
document.getElementById('lists-container').innerHTML =
'<div class="col-12"><div class="alert alert-info">No list keys found.</div></div>';
}
// Update hashes
if (hashes.length > 0) {
console.log(`Found ${hashes.length} hash keys`);
hashes.forEach(item => {
document.getElementById('hashes-container')
.appendChild(createKeyCard(item.key, 'hash', item.value));
});
} else {
document.getElementById('hashes-container').innerHTML =
'<div class="col-12"><div class="alert alert-info">No hash keys found.</div></div>';
}
} else {
console.error('API request failed:', data.error);
throw new Error(data.error || 'Unknown error occurred');
}
})
.catch(error => {
console.error('Error fetching data:', error);
updateConnectionStatus(false, error.message || 'Failed to fetch data from Firefly database');
});
}
// Initial load
refreshData();
</script>
</body>
</html>