Fix scan output file paths and improve notification system

- Save JSON/HTML/ZIP paths to database when scans complete
- Remove orphaned scan-config-id reference causing JS errors
- Add showAlert function to scan_detail.html and scans.html
- Increase notification z-index to 9999 for modal visibility
- Replace inline alert creation with consistent toast notifications
This commit is contained in:
2025-11-20 08:41:02 -06:00
parent 657f4784bf
commit e3b647521e
5 changed files with 58 additions and 24 deletions

View File

@@ -77,12 +77,12 @@ def execute_scan(scan_id: int, config_id: int, db_url: str = None):
# Generate output files (JSON, HTML, ZIP)
logger.info(f"Scan {scan_id}: Generating output files...")
scanner.generate_outputs(report, timestamp)
output_paths = scanner.generate_outputs(report, timestamp)
# Save results to database
logger.info(f"Scan {scan_id}: Saving results to database...")
scan_service = ScanService(session)
scan_service._save_scan_to_db(report, scan_id, status='completed')
scan_service._save_scan_to_db(report, scan_id, status='completed', output_paths=output_paths)
# Evaluate alert rules
logger.info(f"Scan {scan_id}: Evaluating alert rules...")

View File

@@ -308,7 +308,7 @@ class ScanService:
return count
def _save_scan_to_db(self, report: Dict[str, Any], scan_id: int,
status: str = 'completed') -> None:
status: str = 'completed', output_paths: Dict = None) -> None:
"""
Save scan results to database.
@@ -319,6 +319,7 @@ class ScanService:
report: Scan report dictionary from scanner
scan_id: Scan ID to update
status: Final scan status (completed or failed)
output_paths: Dictionary with paths to generated files {'json': Path, 'html': Path, 'zip': Path}
"""
scan = self.db.query(Scan).filter(Scan.id == scan_id).first()
if not scan:
@@ -329,6 +330,15 @@ class ScanService:
scan.duration = report.get('scan_duration')
scan.completed_at = datetime.utcnow()
# Save output file paths
if output_paths:
if 'json' in output_paths:
scan.json_path = str(output_paths['json'])
if 'html' in output_paths:
scan.html_path = str(output_paths['html'])
if 'zip' in output_paths:
scan.zip_path = str(output_paths['zip'])
# Map report data to database models
self._map_report_to_models(report, scan)

View File

@@ -108,7 +108,7 @@
</div>
<!-- Global notification container - always above modals -->
<div id="notification-container" style="position: fixed; top: 20px; right: 20px; z-index: 1100; min-width: 300px;"></div>
<div id="notification-container" style="position: fixed; top: 20px; right: 20px; z-index: 9999; min-width: 300px;"></div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
{% block scripts %}{% endblock %}

View File

@@ -162,6 +162,25 @@
let scanData = null;
let historyChart = null; // Store chart instance to prevent duplicates
// Show alert notification
function showAlert(type, message) {
const container = document.getElementById('notification-container');
const notification = document.createElement('div');
notification.className = `alert alert-${type} alert-dismissible fade show mb-2`;
notification.innerHTML = `
${message}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
container.appendChild(notification);
// Auto-dismiss after 5 seconds
setTimeout(() => {
notification.remove();
}, 5000);
}
// Load scan on page load
document.addEventListener('DOMContentLoaded', function() {
loadScan().then(() => {
@@ -218,7 +237,6 @@
document.getElementById('scan-timestamp').textContent = new Date(scan.timestamp).toLocaleString();
document.getElementById('scan-duration').textContent = scan.duration ? `${scan.duration.toFixed(1)}s` : '-';
document.getElementById('scan-triggered-by').textContent = scan.triggered_by || 'manual';
document.getElementById('scan-config-id').textContent = scan.config_id || '-';
// Status badge
let statusBadge = '';
@@ -439,7 +457,7 @@
window.location.href = '{{ url_for("main.scans") }}';
} catch (error) {
console.error('Error deleting scan:', error);
alert(`Failed to delete scan: ${error.message}`);
showAlert('danger', `Failed to delete scan: ${error.message}`);
// Re-enable button on error
deleteBtn.disabled = false;

View File

@@ -151,6 +151,25 @@
let statusFilter = '';
let totalCount = 0;
// Show alert notification
function showAlert(type, message) {
const container = document.getElementById('notification-container');
const notification = document.createElement('div');
notification.className = `alert alert-${type} alert-dismissible fade show mb-2`;
notification.innerHTML = `
${message}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
container.appendChild(notification);
// Auto-dismiss after 5 seconds
setTimeout(() => {
notification.remove();
}, 5000);
}
// Load initial data when page loads
document.addEventListener('DOMContentLoaded', function() {
loadScans();
@@ -456,15 +475,7 @@
bootstrap.Modal.getInstance(document.getElementById('triggerScanModal')).hide();
// Show success message
const alertDiv = document.createElement('div');
alertDiv.className = 'alert alert-success alert-dismissible fade show mt-3';
alertDiv.innerHTML = `
Scan triggered successfully! (ID: ${data.scan_id})
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
// Insert at the beginning of container-fluid
const container = document.querySelector('.container-fluid');
container.insertBefore(alertDiv, container.firstChild);
showAlert('success', `Scan triggered successfully! (ID: ${data.scan_id})`);
// Refresh scans
loadScans();
@@ -490,23 +501,18 @@
});
if (!response.ok) {
throw new Error('Failed to delete scan');
const data = await response.json();
throw new Error(data.message || 'Failed to delete scan');
}
// Show success message
const alertDiv = document.createElement('div');
alertDiv.className = 'alert alert-success alert-dismissible fade show mt-3';
alertDiv.innerHTML = `
Scan ${scanId} deleted successfully.
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
document.querySelector('.container-fluid').insertBefore(alertDiv, document.querySelector('.row'));
showAlert('success', `Scan ${scanId} deleted successfully.`);
// Refresh scans
loadScans();
} catch (error) {
console.error('Error deleting scan:', error);
alert('Failed to delete scan. Please try again.');
showAlert('danger', `Failed to delete scan: ${error.message}`);
}
}