- Add detailed settings page analysis report (settings.md) - Document identified security vulnerabilities and code quality issues - Provide prioritized improvement recommendations - Document permission and access control issues - Add testing checklist for validation - Track modifications to settings.py, routes.py, and settings.html templates
253 lines
10 KiB
HTML
253 lines
10 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Log Explorer{% endblock %}
|
|
|
|
{% block content %}
|
|
<div style="padding: 20px; max-width: 1400px; margin: 0 auto;">
|
|
<div style="display: flex; align-items: center; gap: 15px; margin-bottom: 30px;">
|
|
<h1 style="margin: 0; color: var(--text-primary, #333); font-size: 2em;">📋 Log Explorer</h1>
|
|
<span style="background: var(--accent-color, #4caf50); color: white; padding: 6px 12px; border-radius: 6px; font-size: 0.85em; font-weight: 600;">Admin</span>
|
|
</div>
|
|
|
|
<div style="display: grid; grid-template-columns: 350px 1fr; gap: 20px; margin-bottom: 20px;">
|
|
<!-- Log Files List -->
|
|
<div style="background: var(--card-bg, white); border: 1px solid var(--border-color, #ddd); border-radius: 8px; overflow: hidden; display: flex; flex-direction: column;">
|
|
<div style="padding: 15px; background: var(--header-bg, #f5f5f5); border-bottom: 1px solid var(--border-color, #ddd); display: flex; align-items: center; gap: 8px;">
|
|
<span style="font-size: 1.2em;">📁</span>
|
|
<strong>Log Files</strong>
|
|
</div>
|
|
|
|
<div id="logs-list" style="flex: 1; overflow-y: auto; padding: 10px; min-height: 400px;">
|
|
<div style="text-align: center; padding: 20px; color: var(--text-secondary, #666);">
|
|
<div style="font-size: 2em; margin-bottom: 10px;">⏳</div>
|
|
<p>Loading log files...</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div style="padding: 10px; border-top: 1px solid var(--border-color, #ddd); text-align: center; font-size: 0.85em; color: var(--text-secondary, #666);">
|
|
<span id="log-count">0</span> files
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Log Content -->
|
|
<div style="background: var(--card-bg, white); border: 1px solid var(--border-color, #ddd); border-radius: 8px; overflow: hidden; display: flex; flex-direction: column;">
|
|
<div style="padding: 15px; background: var(--header-bg, #f5f5f5); border-bottom: 1px solid var(--border-color, #ddd); display: flex; align-items: center; justify-content: space-between;">
|
|
<div style="display: flex; align-items: center; gap: 8px;">
|
|
<span style="font-size: 1.2em;">📄</span>
|
|
<strong id="selected-log-name">Select a log file to view</strong>
|
|
</div>
|
|
<button id="download-log-btn" onclick="downloadCurrentLog()" style="display: none; background: #2196f3; color: white; border: none; padding: 8px 12px; border-radius: 6px; cursor: pointer; font-weight: 600; font-size: 0.9em;">
|
|
⬇️ Download
|
|
</button>
|
|
</div>
|
|
|
|
<div id="log-content" style="flex: 1; overflow-y: auto; padding: 15px; font-family: 'Courier New', monospace; font-size: 0.85em; line-height: 1.5; background: var(--code-bg, #f9f9f9); color: var(--code-text, #333); white-space: pre-wrap; word-wrap: break-word; min-height: 400px;">
|
|
<div style="text-align: center; padding: 40px 20px; color: var(--text-secondary, #666);">
|
|
<div style="font-size: 2em; margin-bottom: 10px;">📖</div>
|
|
<p>Select a log file from the list to view its contents</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Pagination -->
|
|
<div id="pagination-controls" style="padding: 15px; background: var(--header-bg, #f5f5f5); border-top: 1px solid var(--border-color, #ddd); display: none; text-align: center; gap: 10px; display: flex; align-items: center; justify-content: center;">
|
|
<button id="prev-page-btn" onclick="previousPage()" style="background: #2196f3; color: white; border: none; padding: 8px 12px; border-radius: 4px; cursor: pointer; font-weight: 600;">
|
|
← Previous
|
|
</button>
|
|
<span id="page-info" style="font-weight: 600; color: var(--text-primary, #333);">Page 1 of 1</span>
|
|
<button id="next-page-btn" onclick="nextPage()" style="background: #2196f3; color: white; border: none; padding: 8px 12px; border-radius: 4px; cursor: pointer; font-weight: 600;">
|
|
Next →
|
|
</button>
|
|
<span id="lines-info" style="margin-left: auto; font-size: 0.9em; color: var(--text-secondary, #666);">0 total lines</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
let currentLogFile = null;
|
|
let currentPage = 1;
|
|
let totalPages = 1;
|
|
|
|
// Load log files list on page load
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
loadLogsList();
|
|
});
|
|
|
|
function loadLogsList() {
|
|
fetch('/api/logs/list')
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
renderLogsList(data.logs);
|
|
} else {
|
|
document.getElementById('logs-list').innerHTML = '<div style="padding: 20px; color: #d32f2f; text-align: center;">Failed to load logs</div>';
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error loading logs list:', error);
|
|
document.getElementById('logs-list').innerHTML = '<div style="padding: 20px; color: #d32f2f; text-align: center;">Error: ' + error.message + '</div>';
|
|
});
|
|
}
|
|
|
|
function renderLogsList(logs) {
|
|
const logsList = document.getElementById('logs-list');
|
|
|
|
if (logs.length === 0) {
|
|
logsList.innerHTML = '<div style="padding: 20px; text-align: center; color: var(--text-secondary, #666);">No log files found</div>';
|
|
document.getElementById('log-count').textContent = '0';
|
|
return;
|
|
}
|
|
|
|
let html = '';
|
|
logs.forEach(log => {
|
|
html += `
|
|
<div onclick="viewLog('${log.name}')" style="padding: 12px; border-bottom: 1px solid var(--border-color, #ddd); cursor: pointer; transition: all 0.2s; background: var(--item-bg, transparent);" class="log-item" onmouseover="this.style.background='var(--hover-bg, #f0f0f0)'" onmouseout="this.style.background='var(--item-bg, transparent)'">
|
|
<div style="display: flex; align-items: center; gap: 8px;">
|
|
<span>📄</span>
|
|
<div style="flex: 1; min-width: 0;">
|
|
<div style="font-weight: 600; color: var(--text-primary, #333); word-break: break-word;">${log.name}</div>
|
|
<div style="font-size: 0.8em; color: var(--text-secondary, #666); margin-top: 4px;">
|
|
${log.size_formatted} • ${log.modified}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
});
|
|
|
|
logsList.innerHTML = html;
|
|
document.getElementById('log-count').textContent = logs.length;
|
|
}
|
|
|
|
function viewLog(filename) {
|
|
currentLogFile = filename;
|
|
currentPage = 1;
|
|
loadLogContent(filename);
|
|
}
|
|
|
|
function loadLogContent(filename) {
|
|
const logContent = document.getElementById('log-content');
|
|
logContent.innerHTML = '<div style="text-align: center; padding: 40px 20px;"><div style="font-size: 2em; margin-bottom: 10px;">⏳</div><p>Loading...</p></div>';
|
|
|
|
fetch(`/api/logs/view/${encodeURIComponent(filename)}?page=${currentPage}`)
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
renderLogContent(data);
|
|
} else {
|
|
logContent.innerHTML = `<div style="color: #d32f2f; padding: 20px;">Error: ${data.message}</div>`;
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error loading log content:', error);
|
|
logContent.innerHTML = `<div style="color: #d32f2f; padding: 20px;">Error loading log: ${error.message}</div>`;
|
|
});
|
|
}
|
|
|
|
function renderLogContent(data) {
|
|
const logContent = document.getElementById('log-content');
|
|
const lines = data.lines || [];
|
|
|
|
if (lines.length === 0) {
|
|
logContent.textContent = '(Empty file)';
|
|
} else {
|
|
logContent.textContent = lines.join('');
|
|
}
|
|
|
|
// Update pagination
|
|
totalPages = data.total_pages;
|
|
currentPage = data.current_page;
|
|
|
|
const paginationControls = document.getElementById('pagination-controls');
|
|
if (totalPages > 1) {
|
|
paginationControls.style.display = 'flex';
|
|
document.getElementById('page-info').textContent = `Page ${currentPage} of ${totalPages}`;
|
|
document.getElementById('lines-info').textContent = `${data.total_lines} total lines`;
|
|
document.getElementById('prev-page-btn').disabled = currentPage === 1;
|
|
document.getElementById('next-page-btn').disabled = currentPage === totalPages;
|
|
} else {
|
|
paginationControls.style.display = 'none';
|
|
}
|
|
|
|
// Update header
|
|
document.getElementById('selected-log-name').textContent = data.filename;
|
|
document.getElementById('download-log-btn').style.display = 'block';
|
|
}
|
|
|
|
function previousPage() {
|
|
if (currentPage > 1) {
|
|
currentPage--;
|
|
loadLogContent(currentLogFile);
|
|
}
|
|
}
|
|
|
|
function nextPage() {
|
|
if (currentPage < totalPages) {
|
|
currentPage++;
|
|
loadLogContent(currentLogFile);
|
|
}
|
|
}
|
|
|
|
function downloadCurrentLog() {
|
|
if (!currentLogFile) return;
|
|
|
|
const link = document.createElement('a');
|
|
link.href = `/logs/${currentLogFile}`;
|
|
link.download = currentLogFile;
|
|
document.body.appendChild(link);
|
|
link.click();
|
|
document.body.removeChild(link);
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
#log-content {
|
|
-webkit-user-select: text;
|
|
-moz-user-select: text;
|
|
user-select: text;
|
|
}
|
|
|
|
#logs-list {
|
|
scrollbar-width: thin;
|
|
scrollbar-color: var(--scrollbar-color, #ccc) var(--scrollbar-bg, #f5f5f5);
|
|
}
|
|
|
|
#logs-list::-webkit-scrollbar {
|
|
width: 8px;
|
|
}
|
|
|
|
#logs-list::-webkit-scrollbar-track {
|
|
background: var(--scrollbar-bg, #f5f5f5);
|
|
}
|
|
|
|
#logs-list::-webkit-scrollbar-thumb {
|
|
background: var(--scrollbar-color, #ccc);
|
|
border-radius: 4px;
|
|
}
|
|
|
|
#log-content {
|
|
scrollbar-width: thin;
|
|
scrollbar-color: var(--scrollbar-color, #ccc) var(--scrollbar-bg, #f5f5f5);
|
|
}
|
|
|
|
#log-content::-webkit-scrollbar {
|
|
width: 8px;
|
|
}
|
|
|
|
#log-content::-webkit-scrollbar-track {
|
|
background: var(--scrollbar-bg, #f5f5f5);
|
|
}
|
|
|
|
#log-content::-webkit-scrollbar-thumb {
|
|
background: var(--scrollbar-color, #ccc);
|
|
border-radius: 4px;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
div[style*="display: grid"][style*="grid-template-columns: 350px"] {
|
|
grid-template-columns: 1fr !important;
|
|
}
|
|
}
|
|
</style>
|
|
{% endblock %}
|