You've already forked wp-prometheus
All checks were successful
Create Release Package / build-release (push) Successful in 59s
- Custom Metrics Builder with admin UI for gauge metrics - Support for static values and WordPress option-based values - JSON-based export/import with skip/overwrite/rename modes - Three Grafana dashboard templates (overview, runtime, WooCommerce) - New tabs: Custom Metrics and Dashboards - Reset runtime metrics button Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
617 lines
20 KiB
JavaScript
617 lines
20 KiB
JavaScript
/**
|
|
* WP Prometheus Admin JavaScript
|
|
*
|
|
* @package WP_Prometheus
|
|
*/
|
|
|
|
(function($) {
|
|
'use strict';
|
|
|
|
var importFileContent = null;
|
|
|
|
$(document).ready(function() {
|
|
// License tab handlers.
|
|
initLicenseHandlers();
|
|
|
|
// Custom metrics tab handlers.
|
|
initCustomMetricsHandlers();
|
|
|
|
// Dashboards tab handlers.
|
|
initDashboardsHandlers();
|
|
|
|
// Runtime metrics reset handler.
|
|
initResetRuntimeHandler();
|
|
});
|
|
|
|
/**
|
|
* Initialize license tab handlers.
|
|
*/
|
|
function initLicenseHandlers() {
|
|
// Validate license button.
|
|
$('#wp-prometheus-validate-license').on('click', function(e) {
|
|
e.preventDefault();
|
|
performLicenseAction('wp_prometheus_validate_license', 'Validating...');
|
|
});
|
|
|
|
// Activate license button.
|
|
$('#wp-prometheus-activate-license').on('click', function(e) {
|
|
e.preventDefault();
|
|
performLicenseAction('wp_prometheus_activate_license', 'Activating...');
|
|
});
|
|
|
|
// Regenerate token button.
|
|
$('#wp-prometheus-regenerate-token').on('click', function(e) {
|
|
e.preventDefault();
|
|
if (confirm(wpPrometheus.confirmRegenerateToken)) {
|
|
var newToken = generateToken(32);
|
|
$('#wp_prometheus_auth_token').val(newToken);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Initialize custom metrics tab handlers.
|
|
*/
|
|
function initCustomMetricsHandlers() {
|
|
var $formContainer = $('#wp-prometheus-metric-form-container');
|
|
var $form = $('#wp-prometheus-metric-form');
|
|
var $showFormBtn = $('#show-metric-form');
|
|
|
|
// Show metric form.
|
|
$showFormBtn.on('click', function() {
|
|
resetMetricForm();
|
|
$formContainer.slideDown();
|
|
$(this).hide();
|
|
});
|
|
|
|
// Cancel metric form.
|
|
$('#cancel-metric-form').on('click', function() {
|
|
$formContainer.slideUp();
|
|
$showFormBtn.show();
|
|
// Remove edit parameter from URL.
|
|
if (window.location.search.indexOf('edit=') > -1) {
|
|
window.history.pushState({}, '', window.location.pathname + '?page=wp-prometheus&tab=custom');
|
|
}
|
|
});
|
|
|
|
// Value type toggle.
|
|
$('input[name="value_type"]').on('change', function() {
|
|
var valueType = $(this).val();
|
|
if (valueType === 'option') {
|
|
$('#option-config-row').show();
|
|
$('#static-values-row').hide();
|
|
} else {
|
|
$('#option-config-row').hide();
|
|
$('#static-values-row').show();
|
|
}
|
|
});
|
|
|
|
// Add label.
|
|
$('#add-label').on('click', function() {
|
|
var $container = $('#metric-labels-container');
|
|
var labelCount = $container.find('.metric-label-row').length;
|
|
|
|
if (labelCount >= 5) {
|
|
alert('Maximum 5 labels allowed per metric.');
|
|
return;
|
|
}
|
|
|
|
var $row = $('<div class="metric-label-row">' +
|
|
'<input type="text" name="labels[]" class="regular-text" placeholder="label_name" pattern="[a-zA-Z_][a-zA-Z0-9_]*">' +
|
|
'<button type="button" class="button remove-label">×</button>' +
|
|
'</div>');
|
|
$container.append($row);
|
|
updateValueRows();
|
|
});
|
|
|
|
// Remove label.
|
|
$(document).on('click', '.remove-label', function() {
|
|
$(this).closest('.metric-label-row').remove();
|
|
updateValueRows();
|
|
});
|
|
|
|
// Add value row.
|
|
$('#add-value-row').on('click', function() {
|
|
var $container = $('#metric-values-container');
|
|
var rowCount = $container.find('.metric-value-row').length;
|
|
var labelCount = getLabelCount();
|
|
|
|
var $row = $('<div class="metric-value-row"></div>');
|
|
|
|
// Add label value inputs.
|
|
var labels = getLabels();
|
|
for (var i = 0; i < labelCount; i++) {
|
|
$row.append('<input type="text" name="label_values[' + rowCount + '][]" class="small-text" placeholder="' + (labels[i] || 'value') + '">');
|
|
}
|
|
|
|
// Add metric value input.
|
|
$row.append('<input type="number" name="label_values[' + rowCount + '][]" class="small-text" step="any" placeholder="Value">');
|
|
$row.append('<button type="button" class="button remove-value-row">×</button>');
|
|
|
|
$container.append($row);
|
|
});
|
|
|
|
// Remove value row.
|
|
$(document).on('click', '.remove-value-row', function() {
|
|
$(this).closest('.metric-value-row').remove();
|
|
});
|
|
|
|
// Submit metric form.
|
|
$form.on('submit', function(e) {
|
|
e.preventDefault();
|
|
saveCustomMetric();
|
|
});
|
|
|
|
// Delete metric.
|
|
$(document).on('click', '.delete-metric', function() {
|
|
var id = $(this).data('id');
|
|
if (confirm(wpPrometheus.confirmDelete)) {
|
|
deleteCustomMetric(id);
|
|
}
|
|
});
|
|
|
|
// Export metrics.
|
|
$('#export-metrics').on('click', function() {
|
|
exportMetrics();
|
|
});
|
|
|
|
// Import metrics - trigger file input.
|
|
$('#import-metrics-btn').on('click', function() {
|
|
$('#import-metrics-file').click();
|
|
});
|
|
|
|
// Import file selected.
|
|
$('#import-metrics-file').on('change', function(e) {
|
|
var file = e.target.files[0];
|
|
if (file) {
|
|
var reader = new FileReader();
|
|
reader.onload = function(e) {
|
|
importFileContent = e.target.result;
|
|
$('#import-options').slideDown();
|
|
};
|
|
reader.readAsText(file);
|
|
}
|
|
});
|
|
|
|
// Confirm import.
|
|
$('#confirm-import').on('click', function() {
|
|
var mode = $('input[name="import_mode"]:checked').val();
|
|
importMetrics(importFileContent, mode);
|
|
});
|
|
|
|
// Cancel import.
|
|
$('#cancel-import').on('click', function() {
|
|
$('#import-options').slideUp();
|
|
$('#import-metrics-file').val('');
|
|
importFileContent = null;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Initialize dashboards tab handlers.
|
|
*/
|
|
function initDashboardsHandlers() {
|
|
$(document).on('click', '.download-dashboard', function() {
|
|
var slug = $(this).data('slug');
|
|
downloadDashboard(slug);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Initialize reset runtime metrics handler.
|
|
*/
|
|
function initResetRuntimeHandler() {
|
|
$('#wp-prometheus-reset-runtime').on('click', function() {
|
|
if (confirm(wpPrometheus.confirmReset)) {
|
|
resetRuntimeMetrics();
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Perform a license action via AJAX.
|
|
*
|
|
* @param {string} action AJAX action name.
|
|
* @param {string} message Loading message.
|
|
*/
|
|
function performLicenseAction(action, message) {
|
|
var $spinner = $('#wp-prometheus-license-spinner');
|
|
var $message = $('#wp-prometheus-license-message');
|
|
|
|
$spinner.addClass('is-active');
|
|
$message.hide();
|
|
|
|
$.ajax({
|
|
url: wpPrometheus.ajaxUrl,
|
|
type: 'POST',
|
|
data: {
|
|
action: action,
|
|
nonce: wpPrometheus.nonce
|
|
},
|
|
success: function(response) {
|
|
$spinner.removeClass('is-active');
|
|
|
|
if (response.success) {
|
|
$message
|
|
.removeClass('notice-error')
|
|
.addClass('notice notice-success')
|
|
.html('<p>' + response.data.message + '</p>')
|
|
.show();
|
|
|
|
// Reload page after successful validation/activation.
|
|
setTimeout(function() {
|
|
location.reload();
|
|
}, 1500);
|
|
} else {
|
|
$message
|
|
.removeClass('notice-success')
|
|
.addClass('notice notice-error')
|
|
.html('<p>' + (response.data.message || 'An error occurred.') + '</p>')
|
|
.show();
|
|
}
|
|
},
|
|
error: function() {
|
|
$spinner.removeClass('is-active');
|
|
$message
|
|
.removeClass('notice-success')
|
|
.addClass('notice notice-error')
|
|
.html('<p>Connection error. Please try again.</p>')
|
|
.show();
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Save custom metric via AJAX.
|
|
*/
|
|
function saveCustomMetric() {
|
|
var $spinner = $('#wp-prometheus-metric-spinner');
|
|
var $message = $('#wp-prometheus-metric-message');
|
|
var $form = $('#wp-prometheus-metric-form');
|
|
|
|
$spinner.addClass('is-active');
|
|
$message.hide();
|
|
|
|
var formData = $form.serialize();
|
|
formData += '&action=wp_prometheus_save_custom_metric';
|
|
formData += '&nonce=' + wpPrometheus.customMetricNonce;
|
|
|
|
$.ajax({
|
|
url: wpPrometheus.ajaxUrl,
|
|
type: 'POST',
|
|
data: formData,
|
|
success: function(response) {
|
|
$spinner.removeClass('is-active');
|
|
|
|
if (response.success) {
|
|
$message
|
|
.removeClass('notice-error')
|
|
.addClass('notice notice-success')
|
|
.html('<p>' + response.data.message + '</p>')
|
|
.show();
|
|
|
|
setTimeout(function() {
|
|
window.location.href = window.location.pathname + '?page=wp-prometheus&tab=custom';
|
|
}, 1000);
|
|
} else {
|
|
$message
|
|
.removeClass('notice-success')
|
|
.addClass('notice notice-error')
|
|
.html('<p>' + (response.data.message || 'An error occurred.') + '</p>')
|
|
.show();
|
|
}
|
|
},
|
|
error: function() {
|
|
$spinner.removeClass('is-active');
|
|
$message
|
|
.removeClass('notice-success')
|
|
.addClass('notice notice-error')
|
|
.html('<p>Connection error. Please try again.</p>')
|
|
.show();
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Delete custom metric via AJAX.
|
|
*
|
|
* @param {string} id Metric ID.
|
|
*/
|
|
function deleteCustomMetric(id) {
|
|
$.ajax({
|
|
url: wpPrometheus.ajaxUrl,
|
|
type: 'POST',
|
|
data: {
|
|
action: 'wp_prometheus_delete_custom_metric',
|
|
nonce: wpPrometheus.customMetricNonce,
|
|
id: id
|
|
},
|
|
success: function(response) {
|
|
if (response.success) {
|
|
$('tr[data-metric-id="' + id + '"]').fadeOut(function() {
|
|
$(this).remove();
|
|
// Check if table is empty.
|
|
if ($('.wp-prometheus-custom-metrics tbody tr').length === 0) {
|
|
location.reload();
|
|
}
|
|
});
|
|
} else {
|
|
alert(response.data.message || 'An error occurred.');
|
|
}
|
|
},
|
|
error: function() {
|
|
alert('Connection error. Please try again.');
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Export metrics via AJAX.
|
|
*/
|
|
function exportMetrics() {
|
|
$.ajax({
|
|
url: wpPrometheus.ajaxUrl,
|
|
type: 'POST',
|
|
data: {
|
|
action: 'wp_prometheus_export_metrics',
|
|
nonce: wpPrometheus.exportNonce
|
|
},
|
|
success: function(response) {
|
|
if (response.success) {
|
|
downloadFile(response.data.json, response.data.filename, 'application/json');
|
|
} else {
|
|
alert(response.data.message || 'An error occurred.');
|
|
}
|
|
},
|
|
error: function() {
|
|
alert('Connection error. Please try again.');
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Import metrics via AJAX.
|
|
*
|
|
* @param {string} json JSON content.
|
|
* @param {string} mode Import mode.
|
|
*/
|
|
function importMetrics(json, mode) {
|
|
var $spinner = $('#wp-prometheus-import-spinner');
|
|
var $message = $('#wp-prometheus-import-message');
|
|
|
|
$spinner.addClass('is-active');
|
|
$message.hide();
|
|
|
|
$.ajax({
|
|
url: wpPrometheus.ajaxUrl,
|
|
type: 'POST',
|
|
data: {
|
|
action: 'wp_prometheus_import_metrics',
|
|
nonce: wpPrometheus.importNonce,
|
|
json: json,
|
|
mode: mode
|
|
},
|
|
success: function(response) {
|
|
$spinner.removeClass('is-active');
|
|
|
|
if (response.success) {
|
|
$message
|
|
.removeClass('notice-error')
|
|
.addClass('notice notice-success')
|
|
.html('<p>' + response.data.message + '</p>')
|
|
.show();
|
|
|
|
$('#import-options').slideUp();
|
|
$('#import-metrics-file').val('');
|
|
importFileContent = null;
|
|
|
|
setTimeout(function() {
|
|
location.reload();
|
|
}, 1500);
|
|
} else {
|
|
$message
|
|
.removeClass('notice-success')
|
|
.addClass('notice notice-error')
|
|
.html('<p>' + (response.data.message || 'An error occurred.') + '</p>')
|
|
.show();
|
|
}
|
|
},
|
|
error: function() {
|
|
$spinner.removeClass('is-active');
|
|
$message
|
|
.removeClass('notice-success')
|
|
.addClass('notice notice-error')
|
|
.html('<p>Connection error. Please try again.</p>')
|
|
.show();
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Download dashboard via AJAX.
|
|
*
|
|
* @param {string} slug Dashboard slug.
|
|
*/
|
|
function downloadDashboard(slug) {
|
|
$.ajax({
|
|
url: wpPrometheus.ajaxUrl,
|
|
type: 'POST',
|
|
data: {
|
|
action: 'wp_prometheus_download_dashboard',
|
|
nonce: wpPrometheus.dashboardNonce,
|
|
slug: slug
|
|
},
|
|
success: function(response) {
|
|
if (response.success) {
|
|
downloadFile(response.data.json, response.data.filename, 'application/json');
|
|
} else {
|
|
alert(response.data.message || 'An error occurred.');
|
|
}
|
|
},
|
|
error: function() {
|
|
alert('Connection error. Please try again.');
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Reset runtime metrics via AJAX.
|
|
*/
|
|
function resetRuntimeMetrics() {
|
|
var $spinner = $('#wp-prometheus-reset-spinner');
|
|
var $message = $('#wp-prometheus-reset-message');
|
|
|
|
$spinner.addClass('is-active');
|
|
$message.hide();
|
|
|
|
$.ajax({
|
|
url: wpPrometheus.ajaxUrl,
|
|
type: 'POST',
|
|
data: {
|
|
action: 'wp_prometheus_reset_runtime_metrics',
|
|
nonce: wpPrometheus.resetRuntimeNonce
|
|
},
|
|
success: function(response) {
|
|
$spinner.removeClass('is-active');
|
|
|
|
if (response.success) {
|
|
$message
|
|
.removeClass('notice-error')
|
|
.addClass('notice notice-success')
|
|
.html('<p>' + response.data.message + '</p>')
|
|
.show();
|
|
} else {
|
|
$message
|
|
.removeClass('notice-success')
|
|
.addClass('notice notice-error')
|
|
.html('<p>' + (response.data.message || 'An error occurred.') + '</p>')
|
|
.show();
|
|
}
|
|
},
|
|
error: function() {
|
|
$spinner.removeClass('is-active');
|
|
$message
|
|
.removeClass('notice-success')
|
|
.addClass('notice notice-error')
|
|
.html('<p>Connection error. Please try again.</p>')
|
|
.show();
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Reset the metric form to default state.
|
|
*/
|
|
function resetMetricForm() {
|
|
var $form = $('#wp-prometheus-metric-form');
|
|
$form[0].reset();
|
|
$('#metric-id').val('');
|
|
$('#wp-prometheus-form-title').text('Add New Metric');
|
|
$('#option-config-row').hide();
|
|
$('#static-values-row').show();
|
|
$('#wp-prometheus-metric-message').hide();
|
|
|
|
// Reset labels to single empty row.
|
|
$('#metric-labels-container').html(
|
|
'<div class="metric-label-row">' +
|
|
'<input type="text" name="labels[]" class="regular-text" placeholder="label_name" pattern="[a-zA-Z_][a-zA-Z0-9_]*">' +
|
|
'<button type="button" class="button remove-label">×</button>' +
|
|
'</div>'
|
|
);
|
|
|
|
// Reset values to single empty row.
|
|
$('#metric-values-container').html(
|
|
'<div class="metric-value-row">' +
|
|
'<input type="number" name="label_values[0][]" class="small-text" step="any" placeholder="Value">' +
|
|
'<button type="button" class="button remove-value-row">×</button>' +
|
|
'</div>'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Update value rows when labels change.
|
|
*/
|
|
function updateValueRows() {
|
|
var labels = getLabels();
|
|
var labelCount = labels.length;
|
|
|
|
$('#metric-values-container .metric-value-row').each(function(rowIndex) {
|
|
var $row = $(this);
|
|
var inputs = $row.find('input').toArray();
|
|
var currentValues = inputs.map(function(input) { return input.value; });
|
|
|
|
// Remove all inputs except the value and button.
|
|
$row.find('input').remove();
|
|
|
|
// Re-add label inputs.
|
|
for (var i = 0; i < labelCount; i++) {
|
|
var val = currentValues[i] || '';
|
|
$row.prepend('<input type="text" name="label_values[' + rowIndex + '][]" class="small-text" placeholder="' + (labels[i] || 'value') + '" value="' + val + '">');
|
|
}
|
|
|
|
// Re-add value input.
|
|
var metricVal = currentValues[currentValues.length - 1] || '';
|
|
$row.find('.remove-value-row').before('<input type="number" name="label_values[' + rowIndex + '][]" class="small-text" step="any" placeholder="Value" value="' + metricVal + '">');
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get current label names.
|
|
*
|
|
* @return {string[]} Array of label names.
|
|
*/
|
|
function getLabels() {
|
|
var labels = [];
|
|
$('#metric-labels-container input[name="labels[]"]').each(function() {
|
|
var val = $(this).val().trim();
|
|
if (val) {
|
|
labels.push(val);
|
|
}
|
|
});
|
|
return labels;
|
|
}
|
|
|
|
/**
|
|
* Get current label count.
|
|
*
|
|
* @return {number} Number of labels.
|
|
*/
|
|
function getLabelCount() {
|
|
return getLabels().length;
|
|
}
|
|
|
|
/**
|
|
* Generate a random token.
|
|
*
|
|
* @param {number} length Token length.
|
|
* @return {string} Generated token.
|
|
*/
|
|
function generateToken(length) {
|
|
var charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
|
var token = '';
|
|
for (var i = 0; i < length; i++) {
|
|
token += charset.charAt(Math.floor(Math.random() * charset.length));
|
|
}
|
|
return token;
|
|
}
|
|
|
|
/**
|
|
* Download a file.
|
|
*
|
|
* @param {string} content File content.
|
|
* @param {string} filename Filename.
|
|
* @param {string} type MIME type.
|
|
*/
|
|
function downloadFile(content, filename, type) {
|
|
var blob = new Blob([content], { type: type });
|
|
var url = URL.createObjectURL(blob);
|
|
var a = document.createElement('a');
|
|
a.href = url;
|
|
a.download = filename;
|
|
document.body.appendChild(a);
|
|
a.click();
|
|
document.body.removeChild(a);
|
|
URL.revokeObjectURL(url);
|
|
}
|
|
})(jQuery);
|