mirror of
https://github.com/getgrav/grav-plugin-admin.git
synced 2025-10-26 00:36:31 +02:00
@@ -2413,7 +2413,7 @@ class Admin
|
||||
*/
|
||||
public function getLogFiles()
|
||||
{
|
||||
$logs = new GravData(['grav.log' => 'Grav System Log', 'email.log' => 'Email Log']);
|
||||
$logs = new GravData(['grav.log' => 'Grav System Log', 'email.log' => 'Email Log', 'scheduler.log' => 'Scheduler Log']);
|
||||
Grav::instance()->fireEvent('onAdminLogFiles', new Event(['logs' => &$logs]));
|
||||
return $logs->toArray();
|
||||
}
|
||||
|
||||
@@ -798,7 +798,7 @@ PLUGIN_ADMIN:
|
||||
STRICT_TWIG_COMPAT_HELP: "Enables deprecated Twig autoescape setting. When disabled, |raw filter is required to output HTML as Twig will autoescape output"
|
||||
SCHEDULER: "Scheduler"
|
||||
SCHEDULER_INSTALL_INSTRUCTIONS: "Install Instructions"
|
||||
SCHEDULER_INSTALLED_READY: "Installed and Ready"
|
||||
SCHEDULER_INSTALLED_READY: "Scheduler Ready"
|
||||
SCHEDULER_CRON_NA: "Cron Not Available for user: <b>%s</b>"
|
||||
SCHEDULER_NOT_ENABLED: "Not Enabled for user: <b>%s</b>"
|
||||
SCHEDULER_SETUP: "Scheduler Setup"
|
||||
@@ -814,7 +814,7 @@ PLUGIN_ADMIN:
|
||||
SCHEDULER_OUTPUT_TYPE_HELP: "Either append to the same file each run, or overwrite the file with each run"
|
||||
SCHEDULER_EMAIL: "Email"
|
||||
SCHEDULER_EMAIL_HELP: "Email to send output to. NOTE: requires output file to be set"
|
||||
SCHEDULER_WARNING: "The scheduler uses your system's crontab system to execute commands. You should use this only if you are an advanced user and know what you are doing. Misconfiguration or abuse can lead to security vulnerabilities."
|
||||
SCHEDULER_WARNING: "The scheduler can use either system crontab or webhook triggers to execute commands. Webhooks are recommended for cloud environments. Only advanced users should configure custom jobs. Misconfiguration or abuse can lead to security vulnerabilities."
|
||||
SECURITY: "Security"
|
||||
XSS_SECURITY: "XSS Security for Content"
|
||||
XSS_WHITELIST_PERMISSIONS: "Whitelist Permissions"
|
||||
|
||||
144
themes/grav/js/scheduler-admin.js
vendored
Normal file
144
themes/grav/js/scheduler-admin.js
vendored
Normal file
@@ -0,0 +1,144 @@
|
||||
/**
|
||||
* Scheduler Admin JavaScript
|
||||
* Handles dynamic loading of scheduler status in admin panel
|
||||
*/
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
// Wait for DOM to be ready
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Check if we're on the scheduler config page
|
||||
const healthStatusEl = document.getElementById('scheduler-health-status');
|
||||
const triggersEl = document.getElementById('scheduler-triggers');
|
||||
|
||||
if (!healthStatusEl && !triggersEl) {
|
||||
return; // Not on scheduler page
|
||||
}
|
||||
|
||||
// Load scheduler status
|
||||
loadSchedulerStatus();
|
||||
|
||||
// Refresh every 30 seconds if page is visible
|
||||
let refreshInterval = setInterval(function() {
|
||||
if (!document.hidden) {
|
||||
loadSchedulerStatus();
|
||||
}
|
||||
}, 30000);
|
||||
|
||||
// Clean up interval when leaving page
|
||||
window.addEventListener('beforeunload', function() {
|
||||
clearInterval(refreshInterval);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Load scheduler status via AJAX
|
||||
*/
|
||||
function loadSchedulerStatus() {
|
||||
const healthStatusEl = document.getElementById('scheduler-health-status');
|
||||
const triggersEl = document.getElementById('scheduler-triggers');
|
||||
|
||||
// Get the admin base URL
|
||||
const adminBase = GravAdmin ? GravAdmin.config.base_url_relative : '/admin';
|
||||
const nonce = GravAdmin ? GravAdmin.config.admin_nonce : '';
|
||||
|
||||
// Make AJAX request
|
||||
fetch(adminBase + '/scheduler/status', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
'Admin-Nonce': nonce
|
||||
},
|
||||
credentials: 'same-origin'
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
// Update health status
|
||||
if (healthStatusEl && data.health) {
|
||||
healthStatusEl.innerHTML = data.health;
|
||||
healthStatusEl.classList.remove('text-muted');
|
||||
}
|
||||
|
||||
// Update triggers
|
||||
if (triggersEl && data.triggers) {
|
||||
triggersEl.innerHTML = data.triggers;
|
||||
triggersEl.classList.remove('text-muted');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error loading scheduler status:', error);
|
||||
|
||||
// Show error message
|
||||
if (healthStatusEl) {
|
||||
healthStatusEl.innerHTML = '<div class="alert alert-danger">Failed to load status</div>';
|
||||
}
|
||||
if (triggersEl) {
|
||||
triggersEl.innerHTML = '<div class="alert alert-danger">Failed to load triggers</div>';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Test scheduler webhook
|
||||
*/
|
||||
window.testSchedulerWebhook = function() {
|
||||
const token = document.querySelector('input[name="data[scheduler][modern][webhook][token]"]')?.value;
|
||||
|
||||
if (!token) {
|
||||
alert('Please set a webhook token first');
|
||||
return;
|
||||
}
|
||||
|
||||
const siteUrl = window.location.origin;
|
||||
const webhookUrl = siteUrl + '/scheduler/webhook';
|
||||
|
||||
fetch(webhookUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': 'Bearer ' + token,
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
alert('Webhook test successful! Jobs run: ' + (data.jobs_run || 0));
|
||||
} else {
|
||||
alert('Webhook test failed: ' + (data.message || 'Unknown error'));
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
alert('Webhook test error: ' + error.message);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate secure token
|
||||
*/
|
||||
window.generateSchedulerToken = function() {
|
||||
const tokenField = document.querySelector('input[name="data[scheduler][modern][webhook][token]"]');
|
||||
|
||||
if (!tokenField) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate random token (32 bytes = 64 hex chars)
|
||||
const array = new Uint8Array(32);
|
||||
crypto.getRandomValues(array);
|
||||
const token = Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
|
||||
|
||||
tokenField.value = token;
|
||||
|
||||
// Trigger change event
|
||||
const event = new Event('change', { bubbles: true });
|
||||
tokenField.dispatchEvent(event);
|
||||
};
|
||||
|
||||
})();
|
||||
@@ -0,0 +1,33 @@
|
||||
{% extends "forms/field.html.twig" %}
|
||||
|
||||
{% block field %}
|
||||
<div class="webhook-status-field">
|
||||
{% set plugin_exists = config.plugins['scheduler-webhook'] is defined %}
|
||||
{% set plugin_enabled = plugin_exists and config.plugins['scheduler-webhook'].enabled %}
|
||||
|
||||
{% if not plugin_exists %}
|
||||
{# Plugin not installed #}
|
||||
<div class="alert alert-warning">
|
||||
<strong>Webhook Plugin Required</strong><br>
|
||||
The <code>scheduler-webhook</code> plugin is required for webhook functionality.<br><br>
|
||||
<a class="button button-primary" href="{{ base_url_relative }}/plugins/install/scheduler-webhook">
|
||||
<i class="fa fa-download"></i> Install Plugin Now
|
||||
</a>
|
||||
<span class="hint" style="margin-left: 10px;">or run: <code>bin/gpm install scheduler-webhook</code></span>
|
||||
</div>
|
||||
{% elseif not plugin_enabled %}
|
||||
{# Plugin installed but disabled #}
|
||||
<div class="alert alert-info">
|
||||
<i class="fa fa-info-circle"></i> <strong>Webhook Plugin Installed</strong><br>
|
||||
The scheduler-webhook plugin is installed but disabled.
|
||||
<a href="{{ base_url_relative }}/plugins/scheduler-webhook">Enable it in plugin settings</a> to use webhook functionality.
|
||||
</div>
|
||||
{% else %}
|
||||
{# Plugin installed and enabled #}
|
||||
<div class="alert alert-success">
|
||||
<i class="fa fa-check-circle"></i> <strong>Webhook Plugin Ready!</strong><br>
|
||||
The scheduler-webhook plugin is installed and active. Configure your webhook settings below.
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,5 +1,5 @@
|
||||
{% if custom_admin_footer %}
|
||||
{{ custom_admin_footer|raw }}
|
||||
{% else %}
|
||||
<a href="https://getgrav.org" target="_blank" rel="noopener noreferrer">Grav</a> v<span class="grav-version">{{ constant('GRAV_VERSION') }}</span> - Admin v{{ admin_version }} - {{ "PLUGIN_ADMIN.WAS_MADE_WITH"|t|lower }} <i class="fa fa-heart-o pulse"></i> {{ "PLUGIN_ADMIN.BY"|t|lower }} <a href="https://trilby.media" target="_blank" rel="noopener noreferrer">Trilby Media</a>.
|
||||
<a href="https://getgrav.org" target="_blank" rel="noopener noreferrer"><i class="fa fa-grav"></i> Grav</a> v<span class="grav-version">{{ constant('GRAV_VERSION') }}</span> - Admin v{{ admin_version }} - {{ "PLUGIN_ADMIN.WAS_MADE_WITH"|t|lower }} <i class="fa fa-heart-o pulse"></i> {{ "PLUGIN_ADMIN.BY"|t|lower }} <a href="https://trilby.media" target="_blank" rel="noopener noreferrer">Trilby Media</a>.
|
||||
{% endif %}
|
||||
|
||||
@@ -3,12 +3,30 @@
|
||||
{% set data = admin.data('config/scheduler') %}
|
||||
{% set cron_status = grav.scheduler.isCrontabSetup() %}
|
||||
{% set user = grav.scheduler.whoami() %}
|
||||
{% set webhook_enabled = grav.scheduler.isWebhookEnabled() %}
|
||||
{% set active_triggers = grav.scheduler.getActiveTriggers() %}
|
||||
|
||||
{% if cron_status == 1 %}
|
||||
<div class="alert notice secondary-accent">
|
||||
<div id="show-instructions" class="button button-small"><i class="fa fa-clock-o"></i> {{ "PLUGIN_ADMIN.SCHEDULER_INSTALL_INSTRUCTIONS"|t }}</div>
|
||||
<i class="fa fa-check"></i> {{ "PLUGIN_ADMIN.SCHEDULER_INSTALLED_READY"|t }}
|
||||
</div>
|
||||
{% if active_triggers|length > 0 %}
|
||||
{# We have at least one active trigger method #}
|
||||
{% if 'webhook' in active_triggers and 'cron' not in active_triggers %}
|
||||
{# Webhook only mode #}
|
||||
<div class="alert notice">
|
||||
<i class="fa fa-plug"></i> <strong>Webhook Active</strong> - Scheduler is ready to receive webhook triggers
|
||||
<div id="show-instructions" class="button button-small button-outline float-right"><i class="fa fa-clock-o"></i> {{ "PLUGIN_ADMIN.SCHEDULER_INSTALL_INSTRUCTIONS"|t }}</div>
|
||||
</div>
|
||||
{% elseif 'cron' in active_triggers and 'webhook' in active_triggers %}
|
||||
{# Both cron and webhook #}
|
||||
<div class="alert notice secondary-accent">
|
||||
<i class="fa fa-check"></i> <strong>Cron & Webhook Active</strong> - Scheduler is running via cron and accepts webhook triggers
|
||||
<div id="show-instructions" class="button button-small button-outline float-right"><i class="fa fa-clock-o"></i> {{ "PLUGIN_ADMIN.SCHEDULER_INSTALL_INSTRUCTIONS"|t }}</div>
|
||||
</div>
|
||||
{% elseif 'cron' in active_triggers %}
|
||||
{# Cron only #}
|
||||
<div class="alert notice secondary-accent">
|
||||
<i class="fa fa-check"></i> {{ "PLUGIN_ADMIN.SCHEDULER_INSTALLED_READY"|t }}
|
||||
<div id="show-instructions" class="button button-small button-outline float-right"><i class="fa fa-clock-o"></i> {{ "PLUGIN_ADMIN.SCHEDULER_INSTALL_INSTRUCTIONS"|t }}</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% elseif cron_status == 2 %}
|
||||
<div class="alert warning"> {{ "PLUGIN_ADMIN.SCHEDULER_CRON_NA"|t([user])|raw }}</div>
|
||||
{% else %}
|
||||
@@ -17,7 +35,18 @@
|
||||
|
||||
<div class="alert notice"><i class="fa fa-exclamation-circle"></i> {{ "PLUGIN_ADMIN.SCHEDULER_WARNING"|t([user]) }}</div>
|
||||
|
||||
<div id="cron-install" class="form-border overlay {{ cron_status == 1 ? 'hide' : ''}}">
|
||||
<div id="cron-install" class="form-border overlay {{ (active_triggers|length > 0) ? 'hide' : ''}}">
|
||||
{% if webhook_enabled %}
|
||||
<h3>Webhook Setup</h3>
|
||||
<p>The scheduler is configured to use webhooks. To trigger jobs via webhook:</p>
|
||||
<pre><code>curl -X POST {{ grav.base_url_absolute }}/scheduler/webhook \
|
||||
-H "Authorization: Bearer YOUR_TOKEN"</code></pre>
|
||||
<p>Make sure the <strong>scheduler-webhook</strong> plugin is installed and enabled.</p>
|
||||
|
||||
<hr>
|
||||
<h3>Alternative: Cron Setup</h3>
|
||||
{% endif %}
|
||||
|
||||
<pre><code>{{- grav.scheduler.getCronCommand()|trim -}}</code></pre>
|
||||
|
||||
<p>{{ "PLUGIN_ADMIN.SCHEDULER_POST_INSTRUCTIONS"|t([user])|raw }}</p>
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user