mirror of
https://github.com/getgrav/grav.git
synced 2026-07-12 01:02:45 +02:00
@@ -24,302 +24,90 @@ form:
|
||||
validate:
|
||||
type: commalist
|
||||
|
||||
webhook_status_override:
|
||||
type: display
|
||||
label:
|
||||
content: |
|
||||
<script>
|
||||
(function() {
|
||||
function updateSchedulerStatus() {
|
||||
// Find all notice bars
|
||||
var notices = document.querySelectorAll('.notice');
|
||||
var webhookStatusChecked = false;
|
||||
|
||||
// Check for modern scheduler and webhook settings
|
||||
fetch(window.location.origin + '/grav-editor-pro/scheduler/health')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.webhook_enabled) {
|
||||
notices.forEach(function(notice) {
|
||||
if (notice.textContent.includes('Not Enabled for user:')) {
|
||||
// This is the cron status notice - replace it
|
||||
notice.className = 'notice info';
|
||||
notice.innerHTML = '<i class="fa fa-fw fa-check-circle"></i> <strong>Webhook Active</strong> - Scheduler can be triggered via webhook. Cron is not configured.';
|
||||
}
|
||||
});
|
||||
|
||||
// Also update the main status if it exists
|
||||
var statusDiv = document.querySelector('.cronstatus-status');
|
||||
if (statusDiv && statusDiv.textContent.includes('Not Enabled')) {
|
||||
statusDiv.className = 'cronstatus-status success';
|
||||
statusDiv.innerHTML = '<i class="fa fa-fw fa-check"></i> Webhook Ready';
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.log('Webhook status check failed:', error);
|
||||
});
|
||||
}
|
||||
|
||||
// Run on page load
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', updateSchedulerStatus);
|
||||
} else {
|
||||
updateSchedulerStatus();
|
||||
}
|
||||
|
||||
// Also run after a short delay to catch any late-rendered elements
|
||||
setTimeout(updateSchedulerStatus, 500);
|
||||
})();
|
||||
</script>
|
||||
markdown: false
|
||||
|
||||
status_enhanced:
|
||||
type: display
|
||||
label:
|
||||
content: |
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Check if webhook is enabled
|
||||
var webhookEnabled = document.querySelector('[name="data[scheduler][modern][webhook][enabled]"]:checked');
|
||||
var statusDiv = document.querySelector('.cronstatus-status');
|
||||
|
||||
// Also find the parent notice bar
|
||||
var noticeBar = document.querySelector('.notice.alert');
|
||||
|
||||
if (statusDiv) {
|
||||
var currentStatus = statusDiv.textContent || statusDiv.innerText;
|
||||
var cronReady = currentStatus.includes('Ready');
|
||||
var cronNotEnabled = currentStatus.includes('Not Enabled');
|
||||
|
||||
// Check if scheduler-webhook plugin exists
|
||||
var webhookPluginInstalled = false;
|
||||
fetch(window.location.origin + '/grav-editor-pro/scheduler/health')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
webhookPluginInstalled = true;
|
||||
updateStatusDisplay(data);
|
||||
})
|
||||
.catch(error => {
|
||||
updateStatusDisplay(null);
|
||||
});
|
||||
|
||||
function updateStatusDisplay(healthData) {
|
||||
var isWebhookEnabled = webhookEnabled && webhookEnabled.value == '1';
|
||||
var isWebhookReady = webhookPluginInstalled && isWebhookEnabled && healthData && healthData.webhook_enabled;
|
||||
|
||||
// Update the main status text
|
||||
var mainStatusText = '';
|
||||
var mainStatusClass = '';
|
||||
|
||||
if (cronReady && isWebhookReady) {
|
||||
mainStatusText = 'Cron and Webhook Ready';
|
||||
mainStatusClass = 'success';
|
||||
} else if (cronReady) {
|
||||
mainStatusText = 'Cron Ready';
|
||||
mainStatusClass = 'success';
|
||||
} else if (isWebhookReady) {
|
||||
mainStatusText = 'Webhook Ready (No Cron)';
|
||||
mainStatusClass = 'success'; // Changed from warning to success
|
||||
} else if (cronNotEnabled && !isWebhookReady) {
|
||||
mainStatusText = 'Not Configured';
|
||||
mainStatusClass = 'error';
|
||||
} else {
|
||||
mainStatusText = 'Configuration Pending';
|
||||
mainStatusClass = 'warning';
|
||||
}
|
||||
|
||||
// Update the notice bar if webhooks are ready
|
||||
if (noticeBar && isWebhookReady) {
|
||||
// Change from error (red) to success (green) or info (blue)
|
||||
noticeBar.classList.remove('alert');
|
||||
noticeBar.classList.add('info');
|
||||
|
||||
var noticeIcon = noticeBar.querySelector('i.fa');
|
||||
if (noticeIcon) {
|
||||
noticeIcon.classList.remove('fa-times-circle');
|
||||
noticeIcon.classList.add('fa-check-circle');
|
||||
}
|
||||
|
||||
var noticeText = noticeBar.querySelector('strong') || noticeBar;
|
||||
var username = noticeText.textContent.match(/user:\s*(\w+)/);
|
||||
if (username) {
|
||||
noticeText.innerHTML = 'Webhook Ready for user: <b>' + username[1] + '</b> (Cron not configured)';
|
||||
} else {
|
||||
noticeText.innerHTML = mainStatusText;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the main status div
|
||||
if (statusDiv) {
|
||||
statusDiv.innerHTML = '<i class="fa fa-fw fa-' +
|
||||
(mainStatusClass === 'success' ? 'check' : mainStatusClass === 'warning' ? 'exclamation' : 'times') +
|
||||
'"></i> ' + mainStatusText;
|
||||
statusDiv.className = 'cronstatus-status ' + mainStatusClass;
|
||||
}
|
||||
|
||||
// Update install instructions button/content
|
||||
var installButton = document.querySelector('.cronstatus-install-button');
|
||||
var installDiv = document.querySelector('.cronstatus-install');
|
||||
|
||||
if (installDiv) {
|
||||
var installHtml = '<div class="alert alert-info">';
|
||||
installHtml += '<h4>Setup Instructions:</h4>';
|
||||
|
||||
var hasInstructions = false;
|
||||
|
||||
// Cron setup
|
||||
if (!cronReady) {
|
||||
installHtml += '<p><strong>Option 1: Traditional Cron</strong><br>';
|
||||
installHtml += 'Run: <code>bin/grav scheduler --install</code><br>';
|
||||
installHtml += 'This will add a cron job that runs every minute.</p>';
|
||||
hasInstructions = true;
|
||||
}
|
||||
|
||||
// Webhook setup
|
||||
if (!webhookPluginInstalled) {
|
||||
installHtml += '<p><strong>Option 2: Webhook Support</strong><br>';
|
||||
installHtml += '1. Install plugin: <code>bin/gpm install scheduler-webhook</code><br>';
|
||||
installHtml += '2. Configure webhook token in Advanced Features tab<br>';
|
||||
installHtml += '3. Use webhook URL in your CI/CD or cloud scheduler</p>';
|
||||
hasInstructions = true;
|
||||
} else if (!isWebhookEnabled) {
|
||||
installHtml += '<p><strong>Webhook Plugin Installed</strong><br>';
|
||||
installHtml += 'Enable webhooks in Advanced Features tab and set a secure token.</p>';
|
||||
hasInstructions = true;
|
||||
} else if (isWebhookReady) {
|
||||
installHtml += '<p><strong>✅ Webhook is Active!</strong><br>';
|
||||
installHtml += 'Trigger URL: <code>' + window.location.origin + '/grav-editor-pro/scheduler/webhook</code><br>';
|
||||
installHtml += 'Use with Authorization header: <code>Bearer YOUR_TOKEN</code></p>';
|
||||
|
||||
if (!cronReady) {
|
||||
installHtml += '<p class="text-muted"><small>Note: No cron job configured. Scheduler runs only via webhook triggers.</small></p>';
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasInstructions && cronReady) {
|
||||
installHtml += '<p><strong>✅ Cron is configured and ready!</strong><br>';
|
||||
installHtml += 'The scheduler runs automatically every minute via system cron.</p>';
|
||||
|
||||
}
|
||||
|
||||
installHtml += '</div>';
|
||||
installDiv.innerHTML = installHtml;
|
||||
|
||||
// Update button text based on status
|
||||
if (installButton) {
|
||||
if (cronReady && isWebhookReady) {
|
||||
installButton.innerHTML = '<i class="fa fa-info-circle"></i> Configuration Details';
|
||||
} else if (cronReady || isWebhookReady) {
|
||||
installButton.innerHTML = '<i class="fa fa-plus-circle"></i> Add More Triggers';
|
||||
} else {
|
||||
installButton.innerHTML = '<i class="fa fa-exclamation-triangle"></i> Install Instructions';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
modern_health:
|
||||
type: display
|
||||
label: Health Status
|
||||
content: |
|
||||
<div id="scheduler-health-status">
|
||||
<div class="text-muted">Checking health...</div>
|
||||
<div class="text-muted">Loading...</div>
|
||||
</div>
|
||||
<script>
|
||||
(function() {
|
||||
function loadHealthStatus() {
|
||||
fetch(window.location.origin + '/grav-editor-pro/scheduler/health')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
var statusEl = document.getElementById('scheduler-health-status');
|
||||
if (!statusEl) return;
|
||||
|
||||
// Modern card-based layout
|
||||
var statusColor = '#6c757d';
|
||||
var statusLabel = data.status || 'unknown';
|
||||
if (data.status === 'healthy') statusColor = '#28a745';
|
||||
else if (data.status === 'warning') statusColor = '#ffc107';
|
||||
else if (data.status === 'critical') statusColor = '#dc3545';
|
||||
|
||||
var html = '<div style="display: flex; flex-direction: column; gap: 1rem;">';
|
||||
|
||||
// Status card
|
||||
html += '<div style="display: flex; align-items: center; justify-content: space-between; padding: 0.75rem 1rem; background: linear-gradient(135deg, #f8f9fa 0%, #fff 100%); border-radius: 6px; border: 1px solid #e9ecef; box-shadow: 0 1px 3px rgba(0,0,0,0.05);">';
|
||||
html += '<span style="font-weight: 500; color: #495057;">Status:</span>';
|
||||
html += '<span style="background: ' + statusColor + '; color: white; padding: 0.375rem 0.75rem; font-size: 0.875rem; font-weight: 500; border-radius: 4px; text-transform: uppercase; letter-spacing: 0.025em;">' + statusLabel + '</span>';
|
||||
html += '</div>';
|
||||
|
||||
// Info grid
|
||||
html += '<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 0.75rem;">';
|
||||
|
||||
// Last run card
|
||||
html += '<div style="background: white; border: 1px solid #e9ecef; border-radius: 6px; padding: 0.75rem; box-shadow: 0 1px 2px rgba(0,0,0,0.03);">';
|
||||
html += '<div style="color: #6c757d; font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.25rem;">Last Run</div>';
|
||||
if (data.last_run) {
|
||||
var age = data.last_run_age;
|
||||
var ageText = 'just now';
|
||||
if (age > 86400) {
|
||||
ageText = Math.floor(age / 86400) + ' day(s) ago';
|
||||
} else if (age > 3600) {
|
||||
ageText = Math.floor(age / 3600) + ' hour(s) ago';
|
||||
} else if (age > 60) {
|
||||
ageText = Math.floor(age / 60) + ' minute(s) ago';
|
||||
} else if (age > 0) {
|
||||
ageText = age + ' second(s) ago';
|
||||
}
|
||||
html += '<div style="font-size: 1rem; color: #212529; font-weight: 500;">' + ageText + '</div>';
|
||||
} else {
|
||||
html += '<div style="font-size: 1rem; color: #6c757d;">Never</div>';
|
||||
}
|
||||
html += '</div>';
|
||||
|
||||
// Jobs count card
|
||||
html += '<div style="background: white; border: 1px solid #e9ecef; border-radius: 6px; padding: 0.75rem; box-shadow: 0 1px 2px rgba(0,0,0,0.03);">';
|
||||
html += '<div style="color: #6c757d; font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.25rem;">Scheduled Jobs</div>';
|
||||
html += '<div style="font-size: 1rem; color: #212529; font-weight: 500;">' + (data.scheduled_jobs || 0) + '</div>';
|
||||
html += '</div>';
|
||||
|
||||
html += '</div>'; // Close grid
|
||||
|
||||
// Additional info if available
|
||||
if (data.modern_features && data.queue_size !== undefined) {
|
||||
html += '<div style="background: white; border: 1px solid #e9ecef; border-radius: 6px; padding: 0.75rem; box-shadow: 0 1px 2px rgba(0,0,0,0.03);">';
|
||||
html += '<span style="color: #6c757d; font-size: 0.875rem;">Queue Size: </span>';
|
||||
html += '<span style="font-weight: 500;">' + data.queue_size + '</span>';
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
// Failed jobs warning
|
||||
if (data.failed_jobs_24h > 0) {
|
||||
html += '<div style="background: #fff5f5; border: 1px solid #feb2b2; border-radius: 6px; padding: 0.75rem; color: #c53030;">';
|
||||
html += '<strong>⚠️ Failed Jobs (24h):</strong> ' + data.failed_jobs_24h;
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
html += '</div>'; // Close main container
|
||||
statusEl.innerHTML = html;
|
||||
})
|
||||
.catch(error => {
|
||||
var statusEl = document.getElementById('scheduler-health-status');
|
||||
if (statusEl) {
|
||||
statusEl.innerHTML = '<div class="alert alert-warning">Unable to fetch health status. Ensure scheduler-webhook plugin is installed.</div>';
|
||||
}
|
||||
});
|
||||
function renderHealthStatus() {
|
||||
var data = window.schedulerHealthData;
|
||||
var statusEl = document.getElementById('scheduler-health-status');
|
||||
if (!statusEl || !data) return;
|
||||
|
||||
var statusColor = '#6c757d';
|
||||
var statusLabel = data.status || 'unknown';
|
||||
if (data.status === 'healthy') statusColor = '#28a745';
|
||||
else if (data.status === 'warning') statusColor = '#ffc107';
|
||||
else if (data.status === 'critical') statusColor = '#dc3545';
|
||||
|
||||
var html = '<div style="display: flex; flex-direction: column; gap: 1rem;">';
|
||||
|
||||
// Status card
|
||||
html += '<div style="display: flex; align-items: center; justify-content: space-between; padding: 0.75rem 1rem; background: linear-gradient(135deg, #f8f9fa 0%, #fff 100%); border-radius: 6px; border: 1px solid #e9ecef; box-shadow: 0 1px 3px rgba(0,0,0,0.05);">';
|
||||
html += '<span style="font-weight: 500; color: #495057;">Status:</span>';
|
||||
html += '<span style="background: ' + statusColor + '; color: white; padding: 0.375rem 0.75rem; font-size: 0.875rem; font-weight: 500; border-radius: 4px; text-transform: uppercase; letter-spacing: 0.025em;">' + statusLabel + '</span>';
|
||||
html += '</div>';
|
||||
|
||||
// Info grid
|
||||
html += '<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 0.75rem;">';
|
||||
|
||||
// Last run card
|
||||
html += '<div style="background: white; border: 1px solid #e9ecef; border-radius: 6px; padding: 0.75rem; box-shadow: 0 1px 2px rgba(0,0,0,0.03);">';
|
||||
html += '<div style="color: #6c757d; font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.25rem;">Last Run</div>';
|
||||
if (data.last_run) {
|
||||
var age = data.last_run_age;
|
||||
var ageText = 'just now';
|
||||
if (age > 86400) {
|
||||
ageText = Math.floor(age / 86400) + ' day(s) ago';
|
||||
} else if (age > 3600) {
|
||||
ageText = Math.floor(age / 3600) + ' hour(s) ago';
|
||||
} else if (age > 60) {
|
||||
ageText = Math.floor(age / 60) + ' minute(s) ago';
|
||||
} else if (age > 0) {
|
||||
ageText = age + ' second(s) ago';
|
||||
}
|
||||
|
||||
// Load on page ready
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', loadHealthStatus);
|
||||
} else {
|
||||
loadHealthStatus();
|
||||
}
|
||||
|
||||
// Refresh every 30 seconds
|
||||
setInterval(loadHealthStatus, 30000);
|
||||
html += '<div style="font-size: 1rem; color: #212529; font-weight: 500;">' + ageText + '</div>';
|
||||
} else {
|
||||
html += '<div style="font-size: 1rem; color: #6c757d;">Never</div>';
|
||||
}
|
||||
html += '</div>';
|
||||
|
||||
// Jobs count card
|
||||
html += '<div style="background: white; border: 1px solid #e9ecef; border-radius: 6px; padding: 0.75rem; box-shadow: 0 1px 2px rgba(0,0,0,0.03);">';
|
||||
html += '<div style="color: #6c757d; font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.25rem;">Scheduled Jobs</div>';
|
||||
html += '<div style="font-size: 1rem; color: #212529; font-weight: 500;">' + (data.scheduled_jobs || 0) + '</div>';
|
||||
html += '</div>';
|
||||
|
||||
html += '</div>'; // Close grid
|
||||
|
||||
// Queue size
|
||||
if (data.queue_size !== undefined) {
|
||||
html += '<div style="background: white; border: 1px solid #e9ecef; border-radius: 6px; padding: 0.75rem; box-shadow: 0 1px 2px rgba(0,0,0,0.03);">';
|
||||
html += '<span style="color: #6c757d; font-size: 0.875rem;">Queue Size: </span>';
|
||||
html += '<span style="font-weight: 500;">' + data.queue_size + '</span>';
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
// Failed jobs warning
|
||||
if (data.failed_jobs_24h > 0) {
|
||||
html += '<div style="background: #fff5f5; border: 1px solid #feb2b2; border-radius: 6px; padding: 0.75rem; color: #c53030;">';
|
||||
html += '<strong>Failed Jobs (24h):</strong> ' + data.failed_jobs_24h;
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
html += '</div>';
|
||||
statusEl.innerHTML = html;
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', renderHealthStatus);
|
||||
} else {
|
||||
renderHealthStatus();
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
markdown: false
|
||||
@@ -329,93 +117,97 @@ form:
|
||||
label: Active Triggers
|
||||
content: |
|
||||
<div id="scheduler-triggers">
|
||||
<div class="text-muted">Checking triggers...</div>
|
||||
<div class="text-muted">Loading...</div>
|
||||
</div>
|
||||
<script>
|
||||
(function() {
|
||||
function loadTriggers() {
|
||||
// Check cron status from the main status field
|
||||
var cronReady = false;
|
||||
var statusDiv = document.querySelector('.cronstatus-status');
|
||||
if (statusDiv) {
|
||||
var statusText = statusDiv.textContent || statusDiv.innerText;
|
||||
cronReady = statusText.includes('Ready');
|
||||
}
|
||||
|
||||
// Check webhook status
|
||||
fetch(window.location.origin + '/grav-editor-pro/scheduler/health')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
var triggersEl = document.getElementById('scheduler-triggers');
|
||||
if (!triggersEl) return;
|
||||
|
||||
var html = '<div style="display: flex; flex-direction: column; gap: 0.5rem;">';
|
||||
|
||||
// Cron trigger card
|
||||
var cronIcon = cronReady ? '✅' : '❌';
|
||||
var cronStatus = cronReady ? 'Active' : 'Not Configured';
|
||||
var cronStatusColor = cronReady ? '#28a745' : '#6c757d';
|
||||
var cardBg = cronReady ? '#f8f9fa' : '#fff';
|
||||
|
||||
html += '<div style="display: flex; align-items: center; justify-content: space-between; padding: 0.75rem 1rem; background: ' + cardBg + '; border: 1px solid #e9ecef; border-radius: 4px;">';
|
||||
html += '<div style="display: flex; align-items: center; gap: 0.75rem;">';
|
||||
html += '<span style="font-size: 1.25rem; line-height: 1;">' + cronIcon + '</span>';
|
||||
html += '<span style="font-weight: 500; color: #212529; font-size: 1rem;">Cron:</span>';
|
||||
html += '</div>';
|
||||
html += '<span style="background: ' + cronStatusColor + '; color: white; padding: 0.25rem 0.75rem; font-size: 0.875rem; font-weight: 500; border-radius: 3px; text-transform: uppercase; letter-spacing: 0.025em;">' + cronStatus + '</span>';
|
||||
html += '</div>';
|
||||
|
||||
// Webhook trigger card
|
||||
if (data.webhook_enabled) {
|
||||
html += '<div style="display: flex; align-items: center; justify-content: space-between; padding: 0.75rem 1rem; background: #f8f9fa; border: 1px solid #e9ecef; border-radius: 4px;">';
|
||||
html += '<div style="display: flex; align-items: center; gap: 0.75rem;">';
|
||||
html += '<span style="font-size: 1.25rem; line-height: 1;">✅</span>';
|
||||
html += '<span style="font-weight: 500; color: #212529; font-size: 1rem;">Webhook:</span>';
|
||||
html += '</div>';
|
||||
html += '<span style="background: #28a745; color: white; padding: 0.25rem 0.75rem; font-size: 0.875rem; font-weight: 500; border-radius: 3px; text-transform: uppercase; letter-spacing: 0.025em;">ACTIVE</span>';
|
||||
html += '</div>';
|
||||
} else {
|
||||
// Show webhook as not configured/disabled
|
||||
html += '<div style="display: flex; align-items: center; justify-content: space-between; padding: 0.75rem 1rem; background: #fff; border: 1px solid #e9ecef; border-radius: 4px;">';
|
||||
html += '<div style="display: flex; align-items: center; gap: 0.75rem;">';
|
||||
html += '<span style="font-size: 1.25rem; line-height: 1;">⚠️</span>';
|
||||
html += '<span style="font-weight: 500; color: #212529; font-size: 1rem;">Webhook:</span>';
|
||||
html += '</div>';
|
||||
html += '<span style="background: #ffc107; color: #212529; padding: 0.25rem 0.75rem; font-size: 0.875rem; font-weight: 500; border-radius: 3px; text-transform: uppercase; letter-spacing: 0.025em;">DISABLED</span>';
|
||||
html += '</div>';
|
||||
function goToWebhookConfig() {
|
||||
// Find the "Advanced Features" tab and click it
|
||||
var tabs = document.querySelectorAll('.tabs-nav a');
|
||||
for (var i = 0; i < tabs.length; i++) {
|
||||
if (tabs[i].textContent.trim() === 'Advanced Features') {
|
||||
tabs[i].click();
|
||||
// Scroll to "Webhook Configuration" section heading after tab switch
|
||||
setTimeout(function() {
|
||||
var headings = document.querySelectorAll('h1');
|
||||
for (var j = 0; j < headings.length; j++) {
|
||||
if (headings[j].textContent.trim() === 'Webhook Configuration') {
|
||||
headings[j].scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
return;
|
||||
}
|
||||
|
||||
html += '</div>';
|
||||
|
||||
// Add warning if no triggers active
|
||||
if (!cronReady && !data.webhook_enabled) {
|
||||
html += '<div class="alert alert-warning" style="margin-top: 1rem;"><i class="fa fa-exclamation-triangle"></i> No triggers active! Configure cron or enable webhooks.</div>';
|
||||
}
|
||||
|
||||
triggersEl.innerHTML = html;
|
||||
})
|
||||
.catch(error => {
|
||||
var triggersEl = document.getElementById('scheduler-triggers');
|
||||
if (triggersEl) {
|
||||
// Show just cron status if health endpoint not available
|
||||
var html = '<ul class="list-unstyled">';
|
||||
if (cronReady) {
|
||||
html += '<li>✅ <strong>Cron:</strong> <span class="badge badge-success">Active</span></li>';
|
||||
} else {
|
||||
html += '<li>❌ <strong>Cron:</strong> <span class="badge badge-secondary">Not Configured</span></li>';
|
||||
}
|
||||
html += '<li>⚠️ <strong>Webhook:</strong> <span class="badge badge-secondary">Plugin Not Installed</span></li>';
|
||||
html += '</ul>';
|
||||
triggersEl.innerHTML = html;
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 150);
|
||||
return;
|
||||
}
|
||||
|
||||
// Load on page ready
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', loadTriggers);
|
||||
} else {
|
||||
loadTriggers();
|
||||
}
|
||||
}
|
||||
// Expose globally for onclick handlers
|
||||
window.goToWebhookConfig = goToWebhookConfig;
|
||||
|
||||
function renderTriggers() {
|
||||
var data = window.schedulerHealthData;
|
||||
var triggersEl = document.getElementById('scheduler-triggers');
|
||||
if (!triggersEl || !data) return;
|
||||
|
||||
// Check cron status from the main status field
|
||||
var cronReady = false;
|
||||
var statusDiv = document.querySelector('.cronstatus-status');
|
||||
if (statusDiv) {
|
||||
var statusText = statusDiv.textContent || statusDiv.innerText;
|
||||
cronReady = statusText.includes('Ready');
|
||||
}
|
||||
|
||||
var html = '<div style="display: flex; flex-direction: column; gap: 0.5rem;">';
|
||||
|
||||
// Cron trigger card
|
||||
var cronIcon = cronReady ? '<i class="fa fa-check-circle" style="color: #28a745;"></i>' : '<i class="fa fa-times-circle" style="color: #6c757d;"></i>';
|
||||
var cronStatus = cronReady ? 'Active' : 'Not Configured';
|
||||
var cronStatusColor = cronReady ? '#28a745' : '#6c757d';
|
||||
var cardBg = cronReady ? '#f8f9fa' : '#fff';
|
||||
|
||||
html += '<div style="display: flex; align-items: center; justify-content: space-between; padding: 0.75rem 1rem; background: ' + cardBg + '; border: 1px solid #e9ecef; border-radius: 4px;">';
|
||||
html += '<div style="display: flex; align-items: center; gap: 0.75rem;">';
|
||||
html += '<span style="font-size: 1.25rem; line-height: 1;">' + cronIcon + '</span>';
|
||||
html += '<span style="font-weight: 500; color: #212529; font-size: 1rem;">Cron:</span>';
|
||||
html += '</div>';
|
||||
html += '<span style="background: ' + cronStatusColor + '; color: white; padding: 0.25rem 0.75rem; font-size: 0.875rem; font-weight: 500; border-radius: 3px; text-transform: uppercase; letter-spacing: 0.025em;">' + cronStatus + '</span>';
|
||||
html += '</div>';
|
||||
|
||||
// Webhook trigger card
|
||||
if (data.webhook_enabled) {
|
||||
html += '<div style="display: flex; align-items: center; justify-content: space-between; padding: 0.75rem 1rem; background: #f8f9fa; border: 1px solid #e9ecef; border-radius: 4px;">';
|
||||
html += '<div style="display: flex; align-items: center; gap: 0.75rem;">';
|
||||
html += '<span style="font-size: 1.25rem; line-height: 1;"><i class="fa fa-check-circle" style="color: #28a745;"></i></span>';
|
||||
html += '<span style="font-weight: 500; color: #212529; font-size: 1rem;">Webhook:</span>';
|
||||
html += '</div>';
|
||||
html += '<span style="background: #28a745; color: white; padding: 0.25rem 0.75rem; font-size: 0.875rem; font-weight: 500; border-radius: 3px; text-transform: uppercase; letter-spacing: 0.025em;">ACTIVE</span>';
|
||||
html += '</div>';
|
||||
} else {
|
||||
html += '<div onclick="goToWebhookConfig()" style="display: flex; align-items: center; justify-content: space-between; padding: 0.75rem 1rem; background: #fff; border: 1px solid #e9ecef; border-radius: 4px; cursor: pointer;" title="Click to configure webhooks">';
|
||||
html += '<div style="display: flex; align-items: center; gap: 0.75rem;">';
|
||||
html += '<span style="font-size: 1.25rem; line-height: 1;"><i class="fa fa-minus-circle" style="color: #ffc107;"></i></span>';
|
||||
html += '<span style="font-weight: 500; color: #212529; font-size: 1rem;">Webhook:</span>';
|
||||
html += '</div>';
|
||||
html += '<span style="background: #ffc107; color: #212529; padding: 0.25rem 0.75rem; font-size: 0.875rem; font-weight: 500; border-radius: 3px; text-transform: uppercase; letter-spacing: 0.025em;">DISABLED</span>';
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
html += '</div>';
|
||||
|
||||
if (!cronReady && !data.webhook_enabled) {
|
||||
html += '<div class="alert alert-warning" style="margin-top: 1rem; cursor: pointer;" onclick="goToWebhookConfig()">';
|
||||
html += '<i class="fa fa-exclamation-triangle"></i> No triggers active! ';
|
||||
html += '<a onclick="goToWebhookConfig(); event.stopPropagation();" style="cursor: pointer; text-decoration: underline;">Enable webhooks</a>';
|
||||
html += ' or configure cron.</div>';
|
||||
}
|
||||
|
||||
triggersEl.innerHTML = html;
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', renderTriggers);
|
||||
} else {
|
||||
renderTriggers();
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
||||
@@ -12,10 +12,8 @@ status: {}
|
||||
# Custom scheduled jobs
|
||||
custom_jobs: {}
|
||||
|
||||
# Modern scheduler features (disabled by default for backward compatibility)
|
||||
# Modern scheduler features
|
||||
modern:
|
||||
# Enable modern scheduler features
|
||||
enabled: false
|
||||
|
||||
# Number of concurrent workers (1 = sequential execution like legacy)
|
||||
workers: 1
|
||||
|
||||
@@ -598,12 +598,29 @@ class Scheduler
|
||||
|
||||
/**
|
||||
* Check if webhook is enabled
|
||||
*
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isWebhookEnabled(): bool
|
||||
{
|
||||
return $this->webhookEnabled;
|
||||
// Requires both: the config toggle enabled AND the scheduler-webhook plugin installed
|
||||
if (!$this->webhookEnabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$grav = Grav::instance();
|
||||
return (bool) $grav['config']->get('plugins.scheduler-webhook.enabled', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the scheduler-webhook plugin is installed and enabled
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isWebhookPluginReady(): bool
|
||||
{
|
||||
$grav = Grav::instance();
|
||||
return (bool) $grav['config']->get('plugins.scheduler-webhook.enabled', false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1008,7 +1025,7 @@ class Scheduler
|
||||
'failed_jobs_24h' => 0,
|
||||
'scheduled_jobs' => count($enabledJobs),
|
||||
'jobs_due' => $dueJobs,
|
||||
'webhook_enabled' => $this->webhookEnabled,
|
||||
'webhook_enabled' => $this->isWebhookEnabled(),
|
||||
'health_check_enabled' => $this->healthEnabled,
|
||||
'timestamp' => date('c'),
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user