diff --git a/app/web/jobs/scan_job.py b/app/web/jobs/scan_job.py index ace3f8c..d7fcb72 100644 --- a/app/web/jobs/scan_job.py +++ b/app/web/jobs/scan_job.py @@ -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...") diff --git a/app/web/services/scan_service.py b/app/web/services/scan_service.py index 7caedf6..4564986 100644 --- a/app/web/services/scan_service.py +++ b/app/web/services/scan_service.py @@ -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) diff --git a/app/web/templates/base.html b/app/web/templates/base.html index b811060..606a6d6 100644 --- a/app/web/templates/base.html +++ b/app/web/templates/base.html @@ -108,7 +108,7 @@ -
+ {% block scripts %}{% endblock %} diff --git a/app/web/templates/scan_detail.html b/app/web/templates/scan_detail.html index 2111203..926a9ca 100644 --- a/app/web/templates/scan_detail.html +++ b/app/web/templates/scan_detail.html @@ -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} + + `; + + 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; diff --git a/app/web/templates/scans.html b/app/web/templates/scans.html index d15705b..3e5ac3b 100644 --- a/app/web/templates/scans.html +++ b/app/web/templates/scans.html @@ -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} + + `; + + 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}) - - `; - // 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. - - `; - 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}`); } }