mirror of
https://github.com/lucaspalomodevelop/core.git
synced 2026-03-13 08:09:41 +00:00
system: refactor system status mechanism, introduce persistent notifications
Also introduces better sorting with a separate priority value as well as a refactored frontend. Includes some fixes for missing translations as well. To test a banner such as "the system is booting": flock -n -o /var/run/booting cat
This commit is contained in:
parent
761c364743
commit
1fc5a6335e
@ -1,7 +1,7 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Copyright (C) 2019-2022 Deciso B.V.
|
||||
* Copyright (C) 2019-2024 Deciso B.V.
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
@ -34,6 +34,8 @@ use OPNsense\Base\ApiControllerBase;
|
||||
use OPNsense\Core\ACL;
|
||||
use OPNsense\Core\Backend;
|
||||
use OPNsense\Core\Config;
|
||||
use OPNsense\System\SystemStatus;
|
||||
use OPNsense\System\SystemStatusCode;
|
||||
|
||||
/**
|
||||
* Class SystemController
|
||||
@ -68,30 +70,46 @@ class SystemController extends ApiControllerBase
|
||||
$backend = new Backend();
|
||||
$statuses = json_decode(trim($backend->configdRun('system status')), true);
|
||||
if ($statuses) {
|
||||
$order = [-1 => 'Error', 0 => 'Warning', 1 => 'Notice', 2 => 'OK'];
|
||||
|
||||
$order = SystemStatusCode::toValueNameArray();
|
||||
$acl = new ACL();
|
||||
|
||||
foreach ($statuses as $subsystem => $status) {
|
||||
$statuses[$subsystem]['status'] = $order[$status['statusCode']];
|
||||
if (!empty($status['logLocation'])) {
|
||||
if (!$acl->isPageAccessible($this->getUserName(), $status['logLocation'])) {
|
||||
unset($statuses[$subsystem]);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
/* XXX exiting loop causing global endpoint failure */
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
||||
/* Sort on the highest error level after the ACL check */
|
||||
/* Sort on the highest notification (non-persistent) error level after the ACL check */
|
||||
$statusCodes = array_map(function ($v) {
|
||||
return $v['statusCode'];
|
||||
return $v['persistent'] ? SystemStatusCode::OK : $v['statusCode'];
|
||||
}, array_values($statuses));
|
||||
sort($statusCodes);
|
||||
$statuses['System'] = [
|
||||
'status' => $order[$statusCodes[0] ?? 2]
|
||||
|
||||
$response['metadata'] = [
|
||||
/* 'system' represents the status of the top notification after sorting */
|
||||
'system' => [
|
||||
'status' => $order[$statusCodes[0] ?? 2],
|
||||
'message' => gettext('No pending messages'),
|
||||
'title' => gettext('System'),
|
||||
],
|
||||
'translations' => [
|
||||
'dialogTitle' => gettext('System Status'),
|
||||
'dialogCloseButton' => gettext('Close')
|
||||
]
|
||||
];
|
||||
|
||||
// sort on status code and priority where priority is the tie breaker
|
||||
uasort($statuses, function ($a, $b) {
|
||||
if ($a['statusCode'] === $b['statusCode']) {
|
||||
return $a['priority'] <=> $b['priority'];
|
||||
}
|
||||
return $a['statusCode'] <=> $b['statusCode'];
|
||||
});
|
||||
|
||||
foreach ($statuses as &$status) {
|
||||
if (!empty($status['timestamp'])) {
|
||||
$age = time() - $status['timestamp'];
|
||||
@ -150,7 +168,8 @@ class SystemController extends ApiControllerBase
|
||||
}
|
||||
}
|
||||
|
||||
$response = $statuses;
|
||||
$response['subsystems'] = $statuses;
|
||||
unset($response['status']);
|
||||
}
|
||||
|
||||
return $response;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2022 Deciso B.V.
|
||||
* Copyright (C) 2022-2024 Deciso B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -30,25 +30,37 @@ namespace OPNsense\System;
|
||||
|
||||
abstract class AbstractStatus
|
||||
{
|
||||
const STATUS_ERROR = -1;
|
||||
const STATUS_WARNING = 0;
|
||||
const STATUS_NOTICE = 1;
|
||||
const STATUS_OK = 2;
|
||||
protected $internalPriority = 100;
|
||||
protected $internalPersistent = false;
|
||||
protected $internalTitle = null;
|
||||
protected $internalMessage = null;
|
||||
protected $internalLogLocation = null;
|
||||
protected $internalStatus = SystemStatusCode::OK;
|
||||
protected $internalTimestamp = null;
|
||||
|
||||
protected $internalMessage = 'No problems were detected.';
|
||||
protected $internalLogLocation = '';
|
||||
protected $internalStatus = self::STATUS_OK;
|
||||
protected $internalTimestamp = '0';
|
||||
protected $statusStrings = ['notice', 'warning', 'error'];
|
||||
public function getPriority()
|
||||
{
|
||||
return $this->internalPriority;
|
||||
}
|
||||
|
||||
public function getPersistent()
|
||||
{
|
||||
return $this->internalPersistent;
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->internalTitle;
|
||||
}
|
||||
|
||||
public function getStatus()
|
||||
{
|
||||
return $this->internalStatus;
|
||||
}
|
||||
|
||||
public function getMessage($verbose = false)
|
||||
public function getMessage()
|
||||
{
|
||||
return $this->internalMessage;
|
||||
return $this->internalMessage ?? gettext('No problems were detected.');
|
||||
}
|
||||
|
||||
public function getLogLocation()
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2022 Deciso B.V.
|
||||
* Copyright (C) 2022-2024 Deciso B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -30,6 +30,7 @@ namespace OPNsense\System\Status;
|
||||
|
||||
use OPNsense\System\AbstractStatus;
|
||||
use OPNsense\Core\Config;
|
||||
use OPNsense\System\SystemStatusCode;
|
||||
|
||||
class CrashReporterStatus extends AbstractStatus
|
||||
{
|
||||
@ -38,6 +39,8 @@ class CrashReporterStatus extends AbstractStatus
|
||||
$src_logs = array_merge(glob('/var/crash/textdump*'), glob('/var/crash/vmcore*'));
|
||||
$php_log = '/tmp/PHP_errors.log';
|
||||
|
||||
$this->internalPriority = 10;
|
||||
$this->internalTitle = gettext('Crash Reporter');
|
||||
$this->internalLogLocation = '/crash_reporter.php';
|
||||
|
||||
$src_errors = count($src_logs) > 0;
|
||||
@ -61,10 +64,10 @@ class CrashReporterStatus extends AbstractStatus
|
||||
if ($php_errors || $src_errors) {
|
||||
$this->internalMessage = gettext('An issue was detected and can be reviewed using the firmware crash reporter.');
|
||||
if ($php_errors) {
|
||||
$this->internalStatus = Config::getInstance()->object()->system->deployment != 'development' ? static::STATUS_ERROR : static::STATUS_NOTICE;
|
||||
$this->internalStatus = Config::getInstance()->object()->system->deployment != 'development' ? SystemStatusCode::ERROR : SystemStatusCode::NOTICE;
|
||||
}
|
||||
if ($src_errors && $this->internalStatus != static::STATUS_ERROR) {
|
||||
$this->internalStatus = static::STATUS_WARNING;
|
||||
if ($src_errors && $this->internalStatus != SystemStatusCode::ERROR) {
|
||||
$this->internalStatus = SystemStatusCode::WARNING;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2022 Deciso B.V.
|
||||
* Copyright (C) 2022-2024 Deciso B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -29,6 +29,7 @@
|
||||
namespace OPNsense\System\Status;
|
||||
|
||||
use OPNsense\System\AbstractStatus;
|
||||
use OPNsense\System\SystemStatusCode;
|
||||
|
||||
class FirewallStatus extends AbstractStatus
|
||||
{
|
||||
@ -36,11 +37,13 @@ class FirewallStatus extends AbstractStatus
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->internalPriority = 20;
|
||||
$this->internalTitle = gettext('Firewall');
|
||||
$this->internalLogLocation = '/ui/diagnostics/log/core/firewall';
|
||||
|
||||
if (file_exists($this->rules_error)) {
|
||||
$this->internalMessage = file_get_contents($this->rules_error);
|
||||
$this->internalStatus = static::STATUS_ERROR;
|
||||
$this->internalMessage = file_get_contents($this->rules_error); /* XXX */
|
||||
$this->internalStatus = SystemStatusCode::ERROR;
|
||||
$info = stat($this->rules_error);
|
||||
if (!empty($info['mtime'])) {
|
||||
$this->internalTimestamp = $info['mtime'];
|
||||
|
||||
@ -29,14 +29,16 @@
|
||||
namespace OPNsense\System\Status;
|
||||
|
||||
use OPNsense\System\AbstractStatus;
|
||||
use OPNsense\System\SystemStatusCode;
|
||||
use OPNsense\Core\Config;
|
||||
|
||||
class LiveMediaStatus extends AbstractStatus
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
/* XXX historically tied to the dashboard but only given because controller will not allow an omission */
|
||||
$this->internalLogLocation = '/ui/core/dashboard';
|
||||
$this->internalPriority = 2;
|
||||
$this->internalPersistent = true;
|
||||
$this->internalTitle = gettext('Live Media');
|
||||
|
||||
/*
|
||||
* Despite unionfs underneath, / is still not writeable,
|
||||
@ -54,7 +56,7 @@ class LiveMediaStatus extends AbstractStatus
|
||||
return;
|
||||
}
|
||||
|
||||
$this->internalStatus = static::STATUS_WARNING;
|
||||
$this->internalStatus = SystemStatusCode::NOTICE;
|
||||
$this->internalMessage = gettext('You are currently running in live media mode. A reboot will reset the configuration.');
|
||||
if (empty(Config::getInstance()->object()->system->ssh->noauto)) {
|
||||
exec('/bin/pgrep -anx sshd', $output, $retval); /* XXX portability shortcut */
|
||||
|
||||
@ -29,20 +29,22 @@
|
||||
namespace OPNsense\System\Status;
|
||||
|
||||
use OPNsense\System\AbstractStatus;
|
||||
use OPNsense\System\SystemStatusCode;
|
||||
|
||||
class SystemBootingStatus extends AbstractStatus
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
/* XXX historically tied to the dashboard but only given because controller will not allow an omission */
|
||||
$this->internalLogLocation = '/ui/core/dashboard';
|
||||
$this->internalPriority = 1;
|
||||
$this->internalPersistent = true;
|
||||
$this->internalTitle = gettext('System Booting');
|
||||
|
||||
/* XXX boot detection from final class product in config.inc */
|
||||
$fp = fopen('/var/run/booting', 'a+e');
|
||||
if ($fp) {
|
||||
if (!flock($fp, LOCK_SH | LOCK_NB)) {
|
||||
$this->internalMessage = gettext('The system is currently booting. Not all services have been started yet.');
|
||||
$this->internalStatus = static::STATUS_WARNING;
|
||||
$this->internalStatus = SystemStatusCode::WARNING;
|
||||
}
|
||||
fclose($fp);
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2022 Deciso B.V.
|
||||
* Copyright (C) 2022-2024 Deciso B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -63,7 +63,7 @@ class SystemStatus
|
||||
foreach ($statuses as $statusClass) {
|
||||
$obj = new $statusClass();
|
||||
$reflect = new \ReflectionClass($obj);
|
||||
$shortName = str_replace('Status', '', $reflect->getShortName());
|
||||
$shortName = strtolower(str_replace('Status', '', $reflect->getShortName()));
|
||||
$this->objectMap[$shortName] = $obj;
|
||||
|
||||
if ($shortName == 'System') {
|
||||
@ -72,10 +72,13 @@ class SystemStatus
|
||||
}
|
||||
|
||||
$result[$shortName] = [
|
||||
'title' => $obj->getTitle(),
|
||||
'statusCode' => $obj->getStatus(),
|
||||
'message' => $obj->getMessage(),
|
||||
'logLocation' => $obj->getLogLocation(),
|
||||
'timestamp' => $obj->getTimestamp(),
|
||||
'persistent' => $obj->getPersistent(),
|
||||
'priority' => $obj->getPriority(),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2024 Deciso B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
namespace OPNsense\System;
|
||||
|
||||
enum SystemStatusCode:int
|
||||
{
|
||||
case ERROR = -1;
|
||||
case WARNING = 0;
|
||||
case NOTICE = 1;
|
||||
case OK = 2;
|
||||
|
||||
public static function toValueNameArray(): array {
|
||||
$result = [];
|
||||
foreach (self::cases() as $case) {
|
||||
$result[$case->value] = $case->name;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@ -96,22 +96,9 @@
|
||||
initFormAdvancedUI();
|
||||
addMultiSelectClearUI();
|
||||
|
||||
// Create status dialog instance
|
||||
let dialog = new BootstrapDialog({
|
||||
title: '{{ lang._('System Status')}}',
|
||||
buttons: [{
|
||||
label: '{{ lang._('Close') }}',
|
||||
action: function(dialogRef) {
|
||||
dialogRef.close();
|
||||
}
|
||||
}],
|
||||
});
|
||||
|
||||
// artificial delay for UX reasons
|
||||
setTimeout(function () {
|
||||
updateSystemStatus().then((data) => {
|
||||
let status = parseStatus(data);
|
||||
registerStatusDelegate(dialog, status);
|
||||
});
|
||||
updateSystemStatus();
|
||||
}, 500);
|
||||
|
||||
// Register collapsible table headers
|
||||
@ -279,6 +266,12 @@
|
||||
</ul>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- notification banner -->
|
||||
<div class="container-fluid">
|
||||
<div id="notification-banner" class="alert alert-info" style="display: none; margin: 10px 0 10px 0; padding: 10px; text-align: center"></div>
|
||||
</div>
|
||||
|
||||
<!-- page content -->
|
||||
<section class="page-content-main">
|
||||
<div class="container-fluid">
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (C) 2022 Deciso B.V.
|
||||
* Copyright (C) 2022-2024 Deciso B.V.
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
@ -25,53 +25,63 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
function updateStatusDialog(dialog, status, subjectRef = null) {
|
||||
let $ret = $('<div><div><a data-dismiss="modal" class="btn btn-default" style="width:100%;text-align:left;" href="#"><h4><span class="fa fa-circle text-muted"></span> System</h4><p>No pending messages.</p></a></div></div>');
|
||||
function updateStatusDialog(dialog, status) {
|
||||
let $ret = $(`
|
||||
<div>
|
||||
<a data-dismiss="modal" class="btn btn-default" style="width:100%; text-align: left;" href="#">
|
||||
<h4>
|
||||
<span class="fa fa-circle text-muted">
|
||||
</span>
|
||||
|
||||
System
|
||||
</h4>
|
||||
<p>No pending messages.</p>
|
||||
</a>
|
||||
</div>
|
||||
`);
|
||||
|
||||
let $message = $(
|
||||
'<div>' +
|
||||
'<div id="opn-status-list"></div>' +
|
||||
'</div>'
|
||||
);
|
||||
|
||||
for (let subject in status.data) {
|
||||
if (subject === 'System') {
|
||||
continue;
|
||||
}
|
||||
let statusObject = status.data[subject];
|
||||
if (status.data[subject].status == "OK") {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let [shortname, subject] of Object.entries(status)) {
|
||||
$message.find('a').last().addClass('__mb');
|
||||
|
||||
let formattedSubject = subject.replace(/([A-Z])/g, ' $1').trim();
|
||||
if (status.data[subject].age != undefined) {
|
||||
formattedSubject += ' <small>(' + status.data[subject].age + ')</small>';
|
||||
let formattedSubject = subject.title;
|
||||
if (subject.age != undefined) {
|
||||
formattedSubject += ' <small>(' + subject.age + ')</small>';
|
||||
}
|
||||
let listItem = '<a class="btn btn-default" style="width:100%;text-align:left;" href="' + statusObject.logLocation + '">' +
|
||||
'<h4><span class="' + statusObject.icon + '"></span> ' + formattedSubject +
|
||||
'<button id="dismiss-'+ subject + '" class="close"><span aria-hidden="true">×</span></button></h4></div>' +
|
||||
'<p style="white-space: pre-wrap;">' + statusObject.message + '</p></a>';
|
||||
|
||||
let referral = statusObject.logLocation;
|
||||
let ref = subject.logLocation != null ? `href="${subject.logLocation}"` : '';
|
||||
let $closeBtn = `
|
||||
<button id="dismiss-${shortname}" class="close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
`;
|
||||
let $listItem = $(`
|
||||
<a class="btn btn-default" style="width:100%; text-align: left;" ${ref}>
|
||||
<h4>
|
||||
<span class="${subject.icon}"></span>
|
||||
|
||||
${formattedSubject}
|
||||
${subject.persistent ? '' : $closeBtn}
|
||||
</h4>
|
||||
<p style="white-space: pre-wrap;">${subject.message}</p>
|
||||
</a>
|
||||
`)
|
||||
|
||||
$message.find('#opn-status-list').append(listItem);
|
||||
$message.find('#opn-status-list').append($listItem);
|
||||
|
||||
$message.find('#dismiss-' + subject).on('click', function (e) {
|
||||
$message.find('#dismiss-' + shortname).on('click', function (e) {
|
||||
e.preventDefault();
|
||||
$.ajax('/api/core/system/dismissStatus', {
|
||||
type: 'post',
|
||||
data: {'subject': subject},
|
||||
data: {'subject': shortname},
|
||||
dialogRef: dialog,
|
||||
subjectRef: subject,
|
||||
success: function() {
|
||||
updateSystemStatus().then((data) => {
|
||||
let newStatus = parseStatus(data);
|
||||
let $newMessage = updateStatusDialog(this.dialogRef, newStatus, this.subjectRef);
|
||||
this.dialogRef.setMessage($newMessage);
|
||||
$('#system_status').attr("class", newStatus.data['System'].icon);
|
||||
registerStatusDelegate(this.dialogRef, newStatus);
|
||||
});
|
||||
updateSystemStatus(this.dialogRef);
|
||||
}
|
||||
});
|
||||
});
|
||||
@ -81,49 +91,107 @@ function updateStatusDialog(dialog, status, subjectRef = null) {
|
||||
return $ret;
|
||||
}
|
||||
|
||||
function parseStatus(data) {
|
||||
let status = {};
|
||||
let severity = BootstrapDialog.TYPE_PRIMARY;
|
||||
$.each(data, function(subject, statusObject) {
|
||||
switch (statusObject.status) {
|
||||
case "Error":
|
||||
statusObject.icon = 'fa fa-circle text-danger'
|
||||
if (subject != 'System') break;
|
||||
severity = BootstrapDialog.TYPE_DANGER;
|
||||
break;
|
||||
case "Warning":
|
||||
statusObject.icon = 'fa fa-circle text-warning';
|
||||
if (subject != 'System') break;
|
||||
severity = BootstrapDialog.TYPE_WARNING;
|
||||
break;
|
||||
case "Notice":
|
||||
statusObject.icon = 'fa fa-circle text-info';
|
||||
if (subject != 'System') break;
|
||||
severity = BootstrapDialog.TYPE_INFO;
|
||||
break;
|
||||
default:
|
||||
statusObject.icon = 'fa fa-circle text-muted';
|
||||
if (subject != 'System') break;
|
||||
break;
|
||||
}
|
||||
$('#system_status').removeClass().addClass(statusObject.icon);
|
||||
});
|
||||
status.severity = severity;
|
||||
status.data = data;
|
||||
|
||||
return status;
|
||||
function parseStatusIcon(subject) {
|
||||
switch (subject.status) {
|
||||
case "ERROR":
|
||||
subject.icon = 'fa fa-circle text-danger';
|
||||
subject.banner = 'alert-danger';
|
||||
subject.severity = BootstrapDialog.TYPE_DANGER;
|
||||
break;
|
||||
case "WARNING":
|
||||
subject.icon = 'fa fa-circle text-warning';
|
||||
subject.banner ='alert-warning';
|
||||
subject.severity = BootstrapDialog.TYPE_WARNING;
|
||||
break;
|
||||
case "NOTICE":
|
||||
subject.icon = 'fa fa-circle text-info';
|
||||
subject.banner = 'alert-info';
|
||||
subject.severity = BootstrapDialog.TYPE_INFO;
|
||||
break;
|
||||
default:
|
||||
subject.icon = 'fa fa-circle text-muted';
|
||||
subject.banner = 'alert-info';
|
||||
subject.severity = BootstrapDialog.TYPE_PRIMARY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function registerStatusDelegate(dialog, status) {
|
||||
$("#system_status").click(function() {
|
||||
dialog.setMessage(function(dialogRef) {
|
||||
let $message = updateStatusDialog(dialogRef, status);
|
||||
return $message;
|
||||
function fetchSystemStatus() {
|
||||
return new Promise((resolve, reject) => {
|
||||
ajaxGet('/api/core/system/status', {}, function (data) {
|
||||
resolve(data);
|
||||
});
|
||||
dialog.open();
|
||||
});
|
||||
}
|
||||
|
||||
function updateSystemStatus() {
|
||||
return $.ajax('/api/core/system/status', { type: 'get', dataType: 'json' });
|
||||
function parseStatus(data) {
|
||||
let system = data.metadata.system;
|
||||
|
||||
// handle initial page load status icon
|
||||
parseStatusIcon(system);
|
||||
$('#system_status').removeClass().addClass(system.icon);
|
||||
|
||||
let notifications = {};
|
||||
let bannerMessages = {};
|
||||
for (let [shortname, subject] of Object.entries(data.subsystems)) {
|
||||
parseStatusIcon(subject);
|
||||
|
||||
if (subject.status == "OK")
|
||||
continue;
|
||||
|
||||
if (subject.persistent) {
|
||||
bannerMessages[shortname] = subject;
|
||||
} else {
|
||||
notifications[shortname] = subject;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
'banners': bannerMessages,
|
||||
'notifications': notifications
|
||||
};
|
||||
}
|
||||
|
||||
function updateSystemStatus(dialog = null) {
|
||||
fetchSystemStatus().then((data) => {
|
||||
let status = parseStatus(data); // will also update status icon
|
||||
|
||||
if (dialog != null) {
|
||||
dialog.setMessage(function(dialogRef) {
|
||||
return updateStatusDialog(dialogRef, status.notifications);
|
||||
})
|
||||
}
|
||||
|
||||
if (!$.isEmptyObject(status.banners)) {
|
||||
let banner = Object.values(status.banners)[0];
|
||||
$('#notification-banner').addClass(banner.banner).show().html(banner.message);
|
||||
}
|
||||
});
|
||||
|
||||
$("#system_status").click(function() {
|
||||
fetchSystemStatus().then((data) => {
|
||||
let translations = data.metadata.translations;
|
||||
let status = parseStatus(data);
|
||||
|
||||
dialog = new BootstrapDialog({
|
||||
title: translations.dialogTitle,
|
||||
buttons: [{
|
||||
id: 'close',
|
||||
label: translations.dialogCloseButton,
|
||||
action: function(dialogRef) {
|
||||
dialogRef.close();
|
||||
}
|
||||
}],
|
||||
});
|
||||
|
||||
dialog.setMessage(function(dialogRef) {
|
||||
// intentionally do banners first, as these should always show on top
|
||||
// in both cases normal backend sorting applies
|
||||
return updateStatusDialog(dialogRef, {...status.banners, ...status.notifications});
|
||||
})
|
||||
|
||||
dialog.open();
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@ -198,3 +198,8 @@ $aclObj = new \OPNsense\Core\ACL();
|
||||
</ul>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- notification banner -->
|
||||
<div class="container-fluid">
|
||||
<div id="notification-banner" class="alert alert-info" style="display: none; margin: 10px 0 10px 0; padding: 10px; text-align: center"></div>
|
||||
</div>
|
||||
|
||||
@ -183,22 +183,9 @@ $pagetitle .= html_safe(sprintf(' | %s.%s', $config['system']['hostname'], $conf
|
||||
$('html,aside').scrollTop(($(this).offset().top - navbar_center));
|
||||
});
|
||||
|
||||
// Create status dialog instance
|
||||
let dialog = new BootstrapDialog({
|
||||
title: "<?= html_safe(gettext('System Status')) ?>",
|
||||
buttons: [{
|
||||
label: "<?= html_safe(gettext('Close')) ?>",
|
||||
action: function(dialogRef) {
|
||||
dialogRef.close();
|
||||
}
|
||||
}],
|
||||
});
|
||||
|
||||
// artifical delay for UX reasons
|
||||
setTimeout(function () {
|
||||
updateSystemStatus().then((data) => {
|
||||
let status = parseStatus(data);
|
||||
registerStatusDelegate(dialog, status);
|
||||
});
|
||||
updateSystemStatus();
|
||||
}, 500);
|
||||
|
||||
// hook in live menu search
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user