Changes datetime formatting and highlights log severity - only modifies how the page is presented, not how data is stored.
// ==UserScript==
// @name OPNsense Log Enhancer
// @namespace kernkraft235/usability-tweaks
// @version 1.4
// @description Changes datetime formatting and highlights log severity - only modifies how the page is presented, not how data is stored.
// @author kernkraft235
// @match opnsense
// @match router
// @grant none
// @run-at document-end
// @license gpl3
// ==/UserScript==
(function () {
'use strict';
// Inject styles for pulsating and flashing animations
const style = document.createElement('style');
style.textContent = `
@keyframes pulse {
0%, 100% { color: white; opacity: 1; }
50% { background-color: red; color: white; opacity: 1; }
}
@keyframes flash {
0%, 50% { color: red; opacity: 0.9; }
25%, 75% { color: transparent; opacity: 0.1; }
}
.pulsate-red {
animation: pulse 2s infinite;
color: red;
}
.flash-red {
animation: flash 1s infinite;
color: red;
}
.yellow {
color: yellow;
}
.dark-grey {
color: #666;
}
`;
document.head.appendChild(style);
// Function to format datetime strings
function isAndFormatISO8601(text) {
const isoRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}-\d{2}:\d{2}$/;
if (isoRegex.test(text.trim())) {
const date = new Date(text);
const datePart = date.toISOString().slice(2, 10).replace(/-/g, '-');
const weekday = date.toLocaleString('en-US', { weekday: 'short' });
const timePart = date.toLocaleTimeString('en-US', {
hour: '2-digit',
minute: '2-digit',
hour12: false,
});
return `<span style="color: AAAAFF;">${datePart}</span> <span style="color: #555555;">${weekday}</span> <span style="color: #FFFFAA;">${timePart}</span>`; }
return null;
}
// Function to apply colors to statuses
function applyStatusColors() {
document.querySelectorAll('tbody td').forEach((cell) => {
const text = cell.textContent.trim().toLowerCase();
if (!cell.dataset.styled) {
if (text.includes('error')) {
cell.classList.add('pulsate-red');
} else if (text.includes('critical')) {
cell.classList.add('flash-red');
} else if (text.includes('warning')) {
cell.classList.add('yellow');
} else if (text.includes('debug')) {
cell.classList.add('dark-grey');
}
cell.dataset.styled = 'true'; // Prevent re-styling
}
});
}
// Reformat all matching datetime strings in table cells
function reformatDates() {
document.querySelectorAll('td.text-left').forEach((cell) => {
if (!cell.dataset.formatted) {
const formatted = isAndFormatISO8601(cell.textContent);
if (formatted) {
cell.innerHTML = formatted;
cell.dataset.formatted = 'true'; // Prevent reformatting
}
}
});
}
// Observe changes in the DOM to reformat dynamically added elements
const observer = new MutationObserver(() => {
reformatDates();
applyStatusColors();
});
// Start observing the body for changes
observer.observe(document.body, { childList: true, subtree: true });
// Initial run for existing table cells
reformatDates();
applyStatusColors();
})();