304 lines
11 KiB
HTML
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> |