1. app/web/utils/validators.py - Added 'finalizing' to valid_statuses list
2. app/web/models.py - Updated status field comment to document all valid statuses
3. app/web/jobs/scan_job.py
- Added transition to 'finalizing' status before output file generation
- Sets current_phase = 'generating_outputs' during this phase
- Wrapped output generation in try-except with proper error handling
- If output generation fails, scan is marked 'completed' with warning message (scan data is still valid)
4. app/web/api/scans.py
- Added _recover_orphaned_scan() helper function for smart recovery
- Modified stop_running_scan() to:
- Allow stopping scans with status 'running' OR 'finalizing'
- When scanner not in registry, perform smart recovery instead of returning 404
- Smart recovery checks for output files and marks as 'completed' if found, 'cancelled' if not
5. app/web/services/scan_service.py
- Enhanced cleanup_orphaned_scans() with smart recovery logic
- Now finds scans in both 'running' and 'finalizing' status
- Returns dict with stats: {'recovered': N, 'failed': N, 'total': N}
6. app/web/app.py - Updated caller to handle new dict return type from cleanup_orphaned_scans()
Expected Behavior Now
1. Normal scan flow: running → finalizing → completed
2. Stop on active scan: Sends cancel signal, becomes 'cancelled'
3. Stop on orphaned scan with files: Smart recovery → 'completed'
4. Stop on orphaned scan without files: → 'cancelled'
5. App restart with orphans: Startup cleanup uses smart recovery
This commit addresses multiple issues with schedule management and updates
documentation to reflect the transition from YAML-based to database-backed
configuration system.
**Documentation Updates:**
- Update DEPLOYMENT.md to remove all references to YAML config files
- Document that all configurations are now stored in SQLite database
- Update API examples to use config IDs instead of YAML filenames
- Remove configs directory from backup/restore procedures
- Update volume management section to reflect database-only storage
**Cron Expression Handling:**
- Add comprehensive documentation for APScheduler cron format conversion
- Document that from_crontab() accepts standard format (Sunday=0) and converts automatically
- Add validate_cron_expression() helper method with detailed error messages
- Include helpful hints for day-of-week field errors in validation
- Fix all deprecated datetime.utcnow() calls, replace with datetime.now(timezone.utc)
**Timezone-Aware DateTime Fixes:**
- Fix "can't subtract offset-naive and offset-aware datetimes" error
- Add timezone awareness to croniter.get_next() return values
- Make _get_relative_time() defensive to handle both naive and aware datetimes
- Ensure all datetime comparisons use timezone-aware objects
**Schedule Edit UI Fixes:**
- Fix JavaScript error "Cannot set properties of null (setting 'value')"
- Change reference from non-existent 'config-id' to correct 'config-file' element
- Add config_name field to schedule API responses for better UX
- Eagerly load Schedule.config relationship using joinedload()
- Fix AttributeError: use schedule.config.title instead of .name
- Display config title and ID in schedule edit form
**Technical Details:**
- app/web/services/schedule_service.py: 6 datetime.utcnow() fixes, validation enhancements
- app/web/services/scheduler_service.py: Documentation, validation, timezone fixes
- app/web/templates/schedule_edit.html: JavaScript element reference fix
- docs/DEPLOYMENT.md: Complete rewrite of config management sections
Fixes scheduling for Sunday at midnight (cron: 0 0 * * 0)
Fixes schedule edit page JavaScript errors
Improves user experience with config title display
The sites page previously showed total IP count which included duplicates
across multiple sites, leading to inflated numbers. Now displays unique
IP count as the primary metric with duplicate count shown when present.
- Add get_global_ip_stats() method to SiteService for unique/duplicate counts
- Update /api/sites?all=true endpoint to include IP statistics
- Update sites.html to display unique IPs with optional duplicate indicator
- Update API documentation with new response fields
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Allow users to add ports to expected list directly from scan results page
instead of navigating through site config pages. The button appears next
to unexpected ports and updates the site IP configuration via the API.
- Add site_id and site_ip_id to scan result data for linking to config
- Add "Mark Expected" button next to unexpected ports in scan detail view
- Implement markPortExpected() JS function to update site IP settings
- Replace subprocess.run() with Popen for cancellable processes
- Add cancel() method to SneakyScanner with process termination
- Track running scanners in registry for stop signal delivery
- Handle ScanCancelledError to set scan status to 'cancelled'
- Add POST /api/scans/<id>/stop endpoint
- Add 'cancelled' as valid scan status
- Add Stop button to scans list and detail views
- Show cancelled status with warning badge in UI
- Add UDP_SCAN_ENABLED and UDP_PORTS environment variables to control UDP scanning
- UDP scanning disabled by default for faster scans
- Support port ranges (100-200), lists (53,67,68), or mixed formats
- Sort IPs numerically by octets in site management modal
- Add ScanProgress model and progress fields to Scan model
- Implement progress callback in scanner to report phase completion
- Update scan_job to write per-IP results to database during execution
- Add /api/scans/<id>/progress endpoint for progress polling
- Add progress section to scan detail page with live updates
- Progress table shows current phase, completion bar, and per-IP results
- Poll every 3 seconds during active scans
- Sort IPs numerically for proper ordering
- Add database migration for new tables/columns
- Add API endpoint GET /api/scans/by-ip/{ip_address} to retrieve
last 10 scans containing a specific IP
- Add ScanService.get_scans_by_ip() method with ScanIP join query
- Add search box to global navigation header
- Create dedicated search results page at /search/ip
- Update API documentation with new endpoint
Save screenshot_dir to database when scans complete so the directory
is properly cleaned up on scan deletion. Previously the field was never
populated, causing screenshots to remain after deleting scans.
Update sslyze to 6.2.0 and cryptography to 46.0.0 to fix certificate
handling issues with negative serial numbers (RFC 5280 compliance).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add certificate details modal to scan detail page with subject, issuer,
validity dates, serial number, self-signed indicator, SANs, and TLS
version support with expandable cipher suites
- Fix bug where certificate data was not being saved to database due to
incorrect path lookup (was checking http_info['certificate'] instead of
http_info['ssl_tls']['certificate'])
- Update requirements: add sslyze 6.0.0 and upgrade cryptography to >=42.0.0
to fix 'No module named cryptography.x509.verification' error
Display screenshot button in port table when a service has a captured
screenshot. Button opens screenshot in new tab with correct path
including the screenshot directory.
Create comprehensive help page covering:
- Getting started workflow
- Sites and IP management
- Scan configuration
- Running scans manually
- Scheduling automated scans
- Scan comparisons
- Alerts and alert rules
- Webhook configuration
Add Help link with icon to navigation bar.
Add POST /api/alerts/acknowledge-all endpoint to bulk acknowledge all
unacknowledged alerts. Add "Ack All" button to alerts page header with
confirmation dialog for quick dismissal of all pending alerts.
Output files (JSON, HTML, ZIP) are stored outside the static directory,
so download links in scan_detail.html were broken. This adds a /output/
route that serves files from the output directory using send_from_directory
for secure file access. Route requires authentication.
- 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
Save screenshot_dir to database when scans complete so the directory
is properly cleaned up on scan deletion. Previously the field was never
populated, causing screenshots to remain after deleting scans.
Update sslyze to 6.2.0 and cryptography to 46.0.0 to fix certificate
handling issues with negative serial numbers (RFC 5280 compliance).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add certificate details modal to scan detail page with subject, issuer,
validity dates, serial number, self-signed indicator, SANs, and TLS
version support with expandable cipher suites
- Fix bug where certificate data was not being saved to database due to
incorrect path lookup (was checking http_info['certificate'] instead of
http_info['ssl_tls']['certificate'])
- Update requirements: add sslyze 6.0.0 and upgrade cryptography to >=42.0.0
to fix 'No module named cryptography.x509.verification' error
Display screenshot button in port table when a service has a captured
screenshot. Button opens screenshot in new tab with correct path
including the screenshot directory.
Create comprehensive help page covering:
- Getting started workflow
- Sites and IP management
- Scan configuration
- Running scans manually
- Scheduling automated scans
- Scan comparisons
- Alerts and alert rules
- Webhook configuration
Add Help link with icon to navigation bar.
Add POST /api/alerts/acknowledge-all endpoint to bulk acknowledge all
unacknowledged alerts. Add "Ack All" button to alerts page header with
confirmation dialog for quick dismissal of all pending alerts.
Output files (JSON, HTML, ZIP) are stored outside the static directory,
so download links in scan_detail.html were broken. This adds a /output/
route that serves files from the output directory using send_from_directory
for secure file access. Route requires authentication.
- 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
- Fix config_id references to use integers instead of file paths
- Update scan delete response format to include scan_id field
- Add missing read_only field to Settings API responses
- Add missing template fields to Webhook responses
- Correct endpoint count from 80+ to 65+
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Updated init_db.py to use config_id field after database migration,
fixing container startup error on new systems.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>