Phase 3 Step 6: Complete Scheduler Integration with Bug Fixes
Implemented complete scheduler integration with automatic schedule loading, orphaned scan cleanup, and conversion to local timezone for better UX. Backend Changes: - Added load_schedules_on_startup() to load enabled schedules on app start - Implemented cleanup_orphaned_scans() to handle crashed/interrupted scans - Converted scheduler from UTC to local system timezone throughout - Enhanced scheduler service with robust error handling and logging Frontend Changes: - Updated all schedule UI templates to display local time instead of UTC - Improved timezone indicators and user messaging - Removed confusing timezone converter (no longer needed) - Updated quick templates and help text for local time Bug Fixes: - Fixed critical timezone bug causing cron expressions to run at wrong times - Fixed orphaned scans stuck in 'running' status after system crashes - Improved time display clarity across all schedule pages All schedules now use local system time for intuitive scheduling.
This commit is contained in:
@@ -92,10 +92,10 @@
|
||||
<label class="form-label">Quick Templates:</label>
|
||||
<div class="btn-group-vertical btn-group-sm w-100" role="group">
|
||||
<button type="button" class="btn btn-outline-secondary text-start" onclick="setCron('0 0 * * *')">
|
||||
<strong>Daily at Midnight</strong> <code class="float-end">0 0 * * *</code>
|
||||
<strong>Daily at Midnight (local)</strong> <code class="float-end">0 0 * * *</code>
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary text-start" onclick="setCron('0 2 * * *')">
|
||||
<strong>Daily at 2 AM</strong> <code class="float-end">0 2 * * *</code>
|
||||
<strong>Daily at 2 AM (local)</strong> <code class="float-end">0 2 * * *</code>
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary text-start" onclick="setCron('0 */6 * * *')">
|
||||
<strong>Every 6 Hours</strong> <code class="float-end">0 */6 * * *</code>
|
||||
@@ -118,7 +118,7 @@
|
||||
name="cron_expression" placeholder="0 2 * * *"
|
||||
oninput="validateCron()" required>
|
||||
<small class="form-text text-muted">
|
||||
Format: <code>minute hour day month weekday</code> (UTC timezone)
|
||||
Format: <code>minute hour day month weekday</code> (local timezone)
|
||||
</small>
|
||||
</div>
|
||||
|
||||
@@ -130,13 +130,13 @@
|
||||
<div class="col-md-6">
|
||||
<div class="alert alert-info">
|
||||
<strong>Last Run:</strong><br>
|
||||
<span id="last-run">Never</span>
|
||||
<span id="last-run" style="white-space: pre-line;">Never</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="alert alert-info">
|
||||
<strong>Next Run:</strong><br>
|
||||
<span id="next-run">Not scheduled</span>
|
||||
<span id="next-run" style="white-space: pre-line;">Not scheduled</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -248,8 +248,11 @@
|
||||
<li><code>1-5</code> - Range of values</li>
|
||||
</ul>
|
||||
|
||||
<div class="alert alert-warning mt-3">
|
||||
<strong>Note:</strong> All times are in UTC timezone.
|
||||
<div class="alert alert-info mt-3">
|
||||
<strong><i class="bi bi-info-circle"></i> Timezone Information:</strong><br>
|
||||
All cron expressions use your <strong>local system time</strong>.<br><br>
|
||||
<strong>Current local time:</strong> <span id="current-local"></span><br>
|
||||
<strong>Your timezone:</strong> <span id="tz-offset"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -303,13 +306,15 @@ function populateForm(schedule) {
|
||||
document.getElementById('created-at').textContent = new Date(schedule.created_at).toLocaleString();
|
||||
document.getElementById('updated-at').textContent = new Date(schedule.updated_at).toLocaleString();
|
||||
|
||||
// Run times
|
||||
// Run times - show in local time
|
||||
document.getElementById('last-run').textContent = schedule.last_run
|
||||
? formatRelativeTime(schedule.last_run) + ' (' + new Date(schedule.last_run).toLocaleString() + ')'
|
||||
? formatRelativeTime(schedule.last_run) + '\n' +
|
||||
new Date(schedule.last_run).toLocaleString()
|
||||
: 'Never';
|
||||
|
||||
document.getElementById('next-run').textContent = schedule.next_run && schedule.enabled
|
||||
? formatRelativeTime(schedule.next_run) + ' (' + new Date(schedule.next_run).toLocaleString() + ')'
|
||||
? formatRelativeTime(schedule.next_run) + '\n' +
|
||||
new Date(schedule.next_run).toLocaleString()
|
||||
: (schedule.enabled ? 'Calculating...' : 'Disabled');
|
||||
|
||||
// Validate cron
|
||||
@@ -396,17 +401,22 @@ function formatRelativeTime(timestamp) {
|
||||
const diffMs = date - now;
|
||||
const diffMinutes = Math.abs(Math.floor(diffMs / 60000));
|
||||
const diffHours = Math.abs(Math.floor(diffMs / 3600000));
|
||||
const diffDays = Math.abs(Math.floor(diffMs / 86400000));
|
||||
|
||||
if (diffMs < 0) {
|
||||
// Past time
|
||||
if (diffMinutes < 1) return 'Just now';
|
||||
if (diffMinutes < 60) return `${diffMinutes} minutes ago`;
|
||||
if (diffHours < 24) return `${diffHours} hours ago`;
|
||||
return date.toLocaleString();
|
||||
if (diffMinutes < 60) return `${diffMinutes} minute${diffMinutes !== 1 ? 's' : ''} ago`;
|
||||
if (diffHours < 24) return `${diffHours} hour${diffHours !== 1 ? 's' : ''} ago`;
|
||||
if (diffDays === 1) return 'Yesterday';
|
||||
return `${diffDays} days ago`;
|
||||
} else {
|
||||
// Future time
|
||||
if (diffMinutes < 1) return 'In less than a minute';
|
||||
if (diffMinutes < 60) return `In ${diffMinutes} minutes`;
|
||||
if (diffHours < 24) return `In ${diffHours} hours`;
|
||||
return date.toLocaleString();
|
||||
if (diffMinutes < 60) return `In ${diffMinutes} minute${diffMinutes !== 1 ? 's' : ''}`;
|
||||
if (diffHours < 24) return `In ${diffHours} hour${diffHours !== 1 ? 's' : ''}`;
|
||||
if (diffDays === 1) return 'Tomorrow';
|
||||
return `In ${diffDays} days`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -563,7 +573,23 @@ function showNotification(message, type = 'info') {
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
// Update current time display
|
||||
function updateCurrentTime() {
|
||||
const now = new Date();
|
||||
if (document.getElementById('current-local')) {
|
||||
document.getElementById('current-local').textContent = now.toLocaleTimeString();
|
||||
}
|
||||
if (document.getElementById('tz-offset')) {
|
||||
const offset = -now.getTimezoneOffset() / 60;
|
||||
document.getElementById('tz-offset').textContent = `CST (UTC${offset >= 0 ? '+' : ''}${offset})`;
|
||||
}
|
||||
}
|
||||
|
||||
// Load on page load
|
||||
document.addEventListener('DOMContentLoaded', loadSchedule);
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
loadSchedule();
|
||||
updateCurrentTime();
|
||||
setInterval(updateCurrentTime, 1000);
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user