Fix status check colors, add SMTP relay stub

This commit is contained in:
David Duque 2020-04-13 01:16:23 +01:00
parent 0d17caccfe
commit 974c9bba61
No known key found for this signature in database
GPG Key ID: 2F327738A3C0AE3A
4 changed files with 506 additions and 484 deletions

View File

@ -12,6 +12,9 @@ charset = utf-8
trim_trailing_whitespace = true trim_trailing_whitespace = true
insert_final_newline = true insert_final_newline = true
[*.html]
indent_style = tab
[Makefile] [Makefile]
indent_style = tab indent_style = tab
indent_size = 4 indent_size = 4

View File

@ -1,396 +1,409 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{hostname}} - Mail-in-a-Box Control Panel</title> <head>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex, nofollow"> <title>{{hostname}} - Mail-in-a-Box Control Panel</title>
<link rel="stylesheet" href="/admin/assets/bootstrap/css/bootstrap.min.css"> <meta name="robots" content="noindex, nofollow">
<style>
body {
overflow-y: scroll;
padding-bottom: 20px;
}
p { <link rel="stylesheet" href="/admin/assets/bootstrap/css/bootstrap.min.css">
margin-bottom: 1.25em; <style>
} body {
overflow-y: scroll;
padding-bottom: 20px;
}
h1, h2, h3, h4 { p {
font-family: sans-serif; margin-bottom: 1.25em;
font-weight: bold; }
}
h2 { h1,
margin: 1em 0; h2,
} h3,
h4 {
font-family: sans-serif;
font-weight: bold;
}
h3 { h2 {
font-size: 130%; margin: 1em 0;
border-bottom: 1px solid black; }
padding-bottom: 3px;
margin-bottom: 13px;
margin-top: 30px;
}
.panel-heading h3 {
border: none;
padding: 0;
margin: 0;
}
h4 { h3 {
font-size: 110%; font-size: 130%;
margin-bottom: 13px; border-bottom: 1px solid black;
margin-top: 18px; padding-bottom: 3px;
} margin-bottom: 13px;
h4:first-child { margin-top: 30px;
margin-top: 6px; }
}
.admin_panel { .panel-heading h3 {
display: none; border: none;
} padding: 0;
margin: 0;
}
table.table { h4 {
margin: 1.5em 0; font-size: 110%;
} margin-bottom: 13px;
margin-top: 18px;
}
ol li { h4:first-child {
margin-bottom: 1em; margin-top: 6px;
} }
</style>
<link rel="stylesheet" href="/admin/assets/bootstrap/css/bootstrap-theme.min.css">
</head>
<body>
<!--[if lt IE 8]><p>Internet Explorer version 8 or any modern web browser is required to use this website, sorry.<![endif]--> .admin_panel {
<!--[if gt IE 7]><!--> display: none;
}
<div class="navbar navbar-inverse navbar-static-top" role="navigation"> table.table {
<div class="container"> margin: 1.5em 0;
<div class="navbar-header"> }
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">{{hostname}}</a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">System <b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="#system_status" onclick="return show_panel(this);">Status Checks</a></li>
<li><a href="#tls" onclick="return show_panel(this);">TLS (SSL) Certificates</a></li>
<li><a href="#system_backup" onclick="return show_panel(this);">Backup Status</a></li>
<li class="divider"></li>
<li class="dropdown-header">Advanced Pages</li>
<li><a href="#custom_dns" onclick="return show_panel(this);">Custom DNS</a></li>
<li><a href="#external_dns" onclick="return show_panel(this);">External DNS</a></li>
<li><a href="/admin/munin" target="_blank">Munin Monitoring</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Mail <b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="#mail-guide" onclick="return show_panel(this);">Instructions</a></li>
<li><a href="#users" onclick="return show_panel(this);">Users</a></li>
<li><a href="#aliases" onclick="return show_panel(this);">Aliases</a></li>
</ul>
</li>
<li><a href="#sync_guide" onclick="return show_panel(this);">Contacts/Calendar</a></li>
<li><a href="#web" onclick="return show_panel(this);">Web</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li><a href="#" onclick="do_logout(); return false;" style="color: white">Log out</a></li>
</ul>
</div><!--/.navbar-collapse -->
</div>
</div>
<div class="container"> ol li {
<div id="panel_system_status" class="admin_panel"> margin-bottom: 1em;
{% include "system-status.html" %} }
</div> </style>
<link rel="stylesheet" href="/admin/assets/bootstrap/css/bootstrap-theme.min.css">
</head>
<div id="panel_system_backup" class="admin_panel"> <body>
{% include "system-backup.html" %}
</div>
<div id="panel_external_dns" class="admin_panel"> <!--[if lt IE 8]><p>Internet Explorer version 8 or any modern web browser is required to use this website, sorry.<![endif]-->
{% include "external-dns.html" %} <!--[if gt IE 7]><!-->
</div>
<div id="panel_custom_dns" class="admin_panel"> <div class="navbar navbar-inverse navbar-static-top" role="navigation">
{% include "custom-dns.html" %} <div class="container">
</div> <div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">{{hostname}}</a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">System <b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="#system_status" onclick="return show_panel(this);">Status Checks</a></li>
<li><a href="#tls" onclick="return show_panel(this);">TLS (SSL) Certificates</a></li>
<li><a href="#system_backup" onclick="return show_panel(this);">Backup Status</a></li>
<li><a href="#smtp_relays" onclick="return show_panel(this);">SMTP Relays</a></li>
<li class="divider"></li>
<li class="dropdown-header">Advanced Pages</li>
<li><a href="#custom_dns" onclick="return show_panel(this);">Custom DNS</a></li>
<li><a href="#external_dns" onclick="return show_panel(this);">External DNS</a></li>
<li><a href="/admin/munin" target="_blank">Munin Monitoring</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Mail <b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="#mail-guide" onclick="return show_panel(this);">Instructions</a></li>
<li><a href="#users" onclick="return show_panel(this);">Users</a></li>
<li><a href="#aliases" onclick="return show_panel(this);">Aliases</a></li>
</ul>
</li>
<li><a href="#sync_guide" onclick="return show_panel(this);">Contacts/Calendar</a></li>
<li><a href="#web" onclick="return show_panel(this);">Web</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li><a href="#" onclick="do_logout(); return false;" style="color: white">Log out</a></li>
</ul>
</div>
<!--/.navbar-collapse -->
</div>
</div>
<div id="panel_login" class="admin_panel"> <div class="container">
{% include "login.html" %} <div id="panel_system_status" class="admin_panel">
</div> {% include "system-status.html" %}
</div>
<div id="panel_mail-guide" class="admin_panel"> <div id="panel_system_backup" class="admin_panel">
{% include "mail-guide.html" %} {% include "system-backup.html" %}
</div> </div>
<div id="panel_users" class="admin_panel"> <div id="panel_external_dns" class="admin_panel">
{% include "users.html" %} {% include "external-dns.html" %}
</div> </div>
<div id="panel_aliases" class="admin_panel"> <div id="panel_custom_dns" class="admin_panel">
{% include "aliases.html" %} {% include "custom-dns.html" %}
</div> </div>
<div id="panel_sync_guide" class="admin_panel"> <div id="panel_login" class="admin_panel">
{% include "sync-guide.html" %} {% include "login.html" %}
</div> </div>
<div id="panel_web" class="admin_panel"> <div id="panel_mail-guide" class="admin_panel">
{% include "web.html" %} {% include "mail-guide.html" %}
</div> </div>
<div id="panel_tls" class="admin_panel"> <div id="panel_users" class="admin_panel">
{% include "ssl.html" %} {% include "users.html" %}
</div> </div>
<hr> <div id="panel_aliases" class="admin_panel">
{% include "aliases.html" %}
</div>
<footer> <div id="panel_sync_guide" class="admin_panel">
<p>This is a <a href="https://mailinabox.email">Mail-in-a-Box</a>.</p> {% include "sync-guide.html" %}
</footer> </div>
</div> <!-- /container -->
<div id="ajax_loading_indicator" style="display: none; position: fixed; left: 0; top: 0; width: 100%; height: 100%; z-index: 100000; text-align: center; background-color: rgba(255,255,255,.75)"> <div id="panel_web" class="admin_panel">
<div style="margin: 20% auto"> {% include "web.html" %}
<div><span class="fa fa-spinner fa-pulse"></span></div> </div>
<div>Loading...</div>
</div>
</div>
<div id="global_modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="errorModalTitle" aria-hidden="true"> <div id="panel_tls" class="admin_panel">
<div class="modal-dialog modal-sm"> {% include "ssl.html" %}
<div class="modal-content"> </div>
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" id="errorModalTitle"> </h4>
</div>
<div class="modal-body">
<p> </p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">OK</button>
<button type="button" class="btn btn-danger" data-dismiss="modal">Yes</button>
</div>
</div>
</div>
</div>
<script src="/admin/assets/jquery.min.js"></script> <hr>
<script src="/admin/assets/bootstrap/js/bootstrap.min.js"></script>
<script> <footer>
var global_modal_state = null; <p>This is a <a href="https://mailinabox.email">Mail-in-a-Box</a>.</p>
var global_modal_funcs = null; </footer>
</div> <!-- /container -->
$(function() { <div id="ajax_loading_indicator"
$('#global_modal').on('shown.bs.modal', function (e) { style="display: none; position: fixed; left: 0; top: 0; width: 100%; height: 100%; z-index: 100000; text-align: center; background-color: rgba(255,255,255,.75)">
// set focus to first input in the global modal's body <div style="margin: 20% auto">
var input = $('#global_modal .modal-body input'); <div><span class="fa fa-spinner fa-pulse"></span></div>
if (input.length > 0) $(input[0]).focus(); <div>Loading...</div>
}) </div>
$('#global_modal .btn-danger').click(function() { </div>
// Don't take action now. Wait for the modal to be totally hidden
// so that we don't attempt to show another modal while this one
// is closing.
global_modal_state = 0; // OK
})
$('#global_modal .btn-default').click(function() {
global_modal_state = 1; // Cancel
})
$('#global_modal').on('hidden.bs.modal', function (e) {
// do the cancel function
if (global_modal_state == null) global_modal_state = 1; // cancel if the user hit ESC or clicked outside of the modal
if (global_modal_funcs && global_modal_funcs[global_modal_state])
global_modal_funcs[global_modal_state]();
})
})
function show_modal_error(title, message, callback) { <div id="global_modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="errorModalTitle"
$('#global_modal h4').text(title); aria-hidden="true">
$('#global_modal .modal-body').html("<p/>"); <div class="modal-dialog modal-sm">
if (typeof question == 'string') { <div class="modal-content">
$('#global_modal p').text(message); <div class="modal-header">
$('#global_modal .modal-dialog').addClass("modal-sm"); <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
} else { <h4 class="modal-title" id="errorModalTitle"> </h4>
$('#global_modal p').html("").append(message); </div>
$('#global_modal .modal-dialog').removeClass("modal-sm"); <div class="modal-body">
} <p> </p>
$('#global_modal .btn-default').show().text("OK"); </div>
$('#global_modal .btn-danger').hide(); <div class="modal-footer">
global_modal_funcs = [callback, callback]; <button type="button" class="btn btn-default" data-dismiss="modal">OK</button>
global_modal_state = null; <button type="button" class="btn btn-danger" data-dismiss="modal">Yes</button>
$('#global_modal').modal({}); </div>
return false; // handy when called from onclick </div>
} </div>
</div>
function show_modal_confirm(title, question, verb, yes_callback, cancel_callback) { <script src="/admin/assets/jquery.min.js"></script>
$('#global_modal h4').text(title); <script src="/admin/assets/bootstrap/js/bootstrap.min.js"></script>
if (typeof question == 'string') {
$('#global_modal .modal-dialog').addClass("modal-sm");
$('#global_modal .modal-body').html("<p/>");
$('#global_modal p').text(question);
} else {
$('#global_modal .modal-dialog').removeClass("modal-sm");
$('#global_modal .modal-body').html("").append(question);
}
if (typeof verb == 'string') {
$('#global_modal .btn-default').show().text("Cancel");
$('#global_modal .btn-danger').show().text(verb);
} else {
$('#global_modal .btn-default').show().text(verb[1]);
$('#global_modal .btn-danger').show().text(verb[0]);
}
global_modal_funcs = [yes_callback, cancel_callback];
global_modal_state = null;
$('#global_modal').modal({});
return false; // handy when called from onclick
}
var ajax_num_executing_requests = 0; <script>
function ajax_with_indicator(options) { var global_modal_state = null;
setTimeout("if (ajax_num_executing_requests > 0) $('#ajax_loading_indicator').fadeIn()", 100); var global_modal_funcs = null;
function hide_loading_indicator() {
ajax_num_executing_requests--;
if (ajax_num_executing_requests == 0)
$('#ajax_loading_indicator').stop(true).hide(); // stop() prevents an ongoing fade from causing the thing to be shown again after this call
}
var old_success = options.success;
var old_error = options.error;
options.success = function(data) {
hide_loading_indicator();
if (data.status == "error")
show_modal_error("Error", data.message);
else if (old_success)
old_success(data);
};
options.error = function(jqxhr) {
hide_loading_indicator();
if (!old_error)
show_modal_error("Error", "Something went wrong, sorry.")
else
old_error(jqxhr.responseText, jqxhr);
};
ajax_num_executing_requests++;
$.ajax(options);
return false; // handy when called from onclick
}
var api_credentials = ["", ""]; $(function () {
function api(url, method, data, callback, callback_error) { $('#global_modal').on('shown.bs.modal', function (e) {
// from http://www.webtoolkit.info/javascript-base64.html // set focus to first input in the global modal's body
function base64encode(input) { var input = $('#global_modal .modal-body input');
_keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; if (input.length > 0) $(input[0]).focus();
var output = ""; })
var chr1, chr2, chr3, enc1, enc2, enc3, enc4; $('#global_modal .btn-danger').click(function () {
var i = 0; // Don't take action now. Wait for the modal to be totally hidden
while (i < input.length) { // so that we don't attempt to show another modal while this one
chr1 = input.charCodeAt(i++); // is closing.
chr2 = input.charCodeAt(i++); global_modal_state = 0; // OK
chr3 = input.charCodeAt(i++); })
enc1 = chr1 >> 2; $('#global_modal .btn-default').click(function () {
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); global_modal_state = 1; // Cancel
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); })
enc4 = chr3 & 63; $('#global_modal').on('hidden.bs.modal', function (e) {
if (isNaN(chr2)) { // do the cancel function
enc3 = enc4 = 64; if (global_modal_state == null) global_modal_state = 1; // cancel if the user hit ESC or clicked outside of the modal
} else if (isNaN(chr3)) { if (global_modal_funcs && global_modal_funcs[global_modal_state])
enc4 = 64; global_modal_funcs[global_modal_state]();
} })
output = output + })
_keyStr.charAt(enc1) + _keyStr.charAt(enc2) +
_keyStr.charAt(enc3) + _keyStr.charAt(enc4);
}
return output; function show_modal_error(title, message, callback) {
} $('#global_modal h4').text(title);
$('#global_modal .modal-body').html("<p/>");
if (typeof question == 'string') {
$('#global_modal p').text(message);
$('#global_modal .modal-dialog').addClass("modal-sm");
} else {
$('#global_modal p').html("").append(message);
$('#global_modal .modal-dialog').removeClass("modal-sm");
}
$('#global_modal .btn-default').show().text("OK");
$('#global_modal .btn-danger').hide();
global_modal_funcs = [callback, callback];
global_modal_state = null;
$('#global_modal').modal({});
return false; // handy when called from onclick
}
function default_error(text, xhr) { function show_modal_confirm(title, question, verb, yes_callback, cancel_callback) {
if (xhr.status != 403) // else handled below $('#global_modal h4').text(title);
show_modal_error("Error", "Something went wrong, sorry.") if (typeof question == 'string') {
} $('#global_modal .modal-dialog').addClass("modal-sm");
$('#global_modal .modal-body').html("<p/>");
$('#global_modal p').text(question);
} else {
$('#global_modal .modal-dialog').removeClass("modal-sm");
$('#global_modal .modal-body').html("").append(question);
}
if (typeof verb == 'string') {
$('#global_modal .btn-default').show().text("Cancel");
$('#global_modal .btn-danger').show().text(verb);
} else {
$('#global_modal .btn-default').show().text(verb[1]);
$('#global_modal .btn-danger').show().text(verb[0]);
}
global_modal_funcs = [yes_callback, cancel_callback];
global_modal_state = null;
$('#global_modal').modal({});
return false; // handy when called from onclick
}
ajax_with_indicator({ var ajax_num_executing_requests = 0;
url: "/admin" + url, function ajax_with_indicator(options) {
method: method, setTimeout("if (ajax_num_executing_requests > 0) $('#ajax_loading_indicator').fadeIn()", 100);
cache: false, function hide_loading_indicator() {
data: data, ajax_num_executing_requests--;
if (ajax_num_executing_requests == 0)
$('#ajax_loading_indicator').stop(true).hide(); // stop() prevents an ongoing fade from causing the thing to be shown again after this call
}
var old_success = options.success;
var old_error = options.error;
options.success = function (data) {
hide_loading_indicator();
if (data.status == "error")
show_modal_error("Error", data.message);
else if (old_success)
old_success(data);
};
options.error = function (jqxhr) {
hide_loading_indicator();
if (!old_error)
show_modal_error("Error", "Something went wrong, sorry.")
else
old_error(jqxhr.responseText, jqxhr);
};
ajax_num_executing_requests++;
$.ajax(options);
return false; // handy when called from onclick
}
// the custom DNS api sends raw POST/PUT bodies --- prevent URL-encoding var api_credentials = ["", ""];
processData: typeof data != "string", function api(url, method, data, callback, callback_error) {
mimeType: typeof data == "string" ? "text/plain; charset=ascii" : null, // from http://www.webtoolkit.info/javascript-base64.html
function base64encode(input) {
_keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
var output = "";
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
var i = 0;
while (i < input.length) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output +
_keyStr.charAt(enc1) + _keyStr.charAt(enc2) +
_keyStr.charAt(enc3) + _keyStr.charAt(enc4);
}
beforeSend: function(xhr) { return output;
// We don't store user credentials in a cookie to avoid the hassle of CSRF }
// attacks. The Authorization header only gets set in our AJAX calls triggered
// by user actions.
xhr.setRequestHeader(
'Authorization',
'Basic ' + base64encode(api_credentials[0] + ':' + api_credentials[1]));
},
success: callback,
error: callback_error || default_error,
statusCode: {
403: function(xhr) {
// Credentials are no longer valid. Try to login again.
var p = current_panel;
show_panel('login');
switch_back_to_panel = p;
}
}
})
}
var current_panel = null; function default_error(text, xhr) {
var switch_back_to_panel = null; if (xhr.status != 403) // else handled below
function show_panel(panelid) { show_modal_error("Error", "Something went wrong, sorry.")
if (panelid.getAttribute) }
// we might be passed an HTMLElement <a>.
panelid = panelid.getAttribute('href').substring(1);
$('.admin_panel').hide(); ajax_with_indicator({
$('#panel_' + panelid).show(); url: "/admin" + url,
if (typeof localStorage != 'undefined') method: method,
localStorage.setItem("miab-cp-lastpanel", panelid); cache: false,
if (window["show_" + panelid]) data: data,
window["show_" + panelid]();
current_panel = panelid; // the custom DNS api sends raw POST/PUT bodies --- prevent URL-encoding
switch_back_to_panel = null; processData: typeof data != "string",
mimeType: typeof data == "string" ? "text/plain; charset=ascii" : null,
return false; // when called from onclick, cancel navigation beforeSend: function (xhr) {
} // We don't store user credentials in a cookie to avoid the hassle of CSRF
// attacks. The Authorization header only gets set in our AJAX calls triggered
// by user actions.
xhr.setRequestHeader(
'Authorization',
'Basic ' + base64encode(api_credentials[0] + ':' + api_credentials[1]));
},
success: callback,
error: callback_error || default_error,
statusCode: {
403: function (xhr) {
// Credentials are no longer valid. Try to login again.
var p = current_panel;
show_panel('login');
switch_back_to_panel = p;
}
}
})
}
$(function() { var current_panel = null;
// Recall saved user credentials. var switch_back_to_panel = null;
if (typeof sessionStorage != 'undefined' && sessionStorage.getItem("miab-cp-credentials")) function show_panel(panelid) {
api_credentials = sessionStorage.getItem("miab-cp-credentials").split(":"); if (panelid.getAttribute)
else if (typeof localStorage != 'undefined' && localStorage.getItem("miab-cp-credentials")) // we might be passed an HTMLElement <a>.
api_credentials = localStorage.getItem("miab-cp-credentials").split(":"); panelid = panelid.getAttribute('href').substring(1);
// Recall what the user was last looking at. $('.admin_panel').hide();
if (typeof localStorage != 'undefined' && localStorage.getItem("miab-cp-lastpanel")) { $('#panel_' + panelid).show();
show_panel(localStorage.getItem("miab-cp-lastpanel")); if (typeof localStorage != 'undefined')
} else { localStorage.setItem("miab-cp-lastpanel", panelid);
show_panel('login'); if (window["show_" + panelid])
} window["show_" + panelid]();
})
current_panel = panelid;
switch_back_to_panel = null;
return false; // when called from onclick, cancel navigation
}
$(function () {
// Recall saved user credentials.
if (typeof sessionStorage != 'undefined' && sessionStorage.getItem("miab-cp-credentials"))
api_credentials = sessionStorage.getItem("miab-cp-credentials").split(":");
else if (typeof localStorage != 'undefined' && localStorage.getItem("miab-cp-credentials"))
api_credentials = localStorage.getItem("miab-cp-credentials").split(":");
// Recall what the user was last looking at.
if (typeof localStorage != 'undefined' && localStorage.getItem("miab-cp-lastpanel")) {
show_panel(localStorage.getItem("miab-cp-lastpanel"));
} else {
show_panel('login');
}
})
</script>
</body>
</script>
</body>
</html> </html>

View File

@ -0,0 +1,6 @@
<style>
</style>
<h2>SMTP Relays</h2>
<p>Coming Soon™</p>

View File

@ -1,169 +1,169 @@
<h2>System Status Checks</h2> <h2>System Status Checks</h2>
<style> <style>
#system-checks .heading td { #system-checks .heading td {
font-weight: bold; font-weight: bold;
font-size: 120%; font-size: 120%;
padding-top: 1.5em; padding-top: 1.5em;
} }
#system-checks .heading.first td { #system-checks .heading.first td {
border-top: none; border-top: none;
padding-top: 0; padding-top: 0;
} }
#system-checks .status-error td { #system-checks .status-error td {
color: rgb(180, 0, 0); color: rgb(140, 0, 0);
} }
#system-checks .status-warning td { #system-checks .status-warning td {
color: rgb(180, 180, 0); color: rgb(170, 120, 0);
} }
#system-checks .status-ok td { #system-checks .status-ok td {
color: rgb(0, 180, 0); color: rgb(0, 140, 0);
} }
#system-checks div.extra { #system-checks div.extra {
display: none; display: none;
margin-top: 1em; margin-top: 1em;
max-width: 50em; max-width: 50em;
word-wrap: break-word; word-wrap: break-word;
} }
#system-checks a.showhide { #system-checks a.showhide {
display: none; display: none;
font-size: 85%; font-size: 85%;
} }
#system-checks .pre { #system-checks .pre {
margin: 1em; margin: 1em;
font-family: monospace; font-family: monospace;
white-space: pre-wrap; white-space: pre-wrap;
} }
</style> </style>
<div class="row"> <div class="row">
<div class="col-md-push-9 col-md-3"> <div class="col-md-push-9 col-md-3">
<div id="system-reboot-required" style="display: none; margin-bottom: 1em;"> <div id="system-reboot-required" style="display: none; margin-bottom: 1em;">
<button type="button" class="btn btn-danger" onclick="confirm_reboot(); return false;">Reboot Box</button> <button type="button" class="btn btn-danger" onclick="confirm_reboot(); return false;">Reboot Box</button>
<div>No reboot is necessary.</div> <div>No reboot is necessary.</div>
</div> </div>
<div id="system-privacy-setting" style="display: none"> <div id="system-privacy-setting" style="display: none">
<div><a onclick="return enable_privacy(!current_privacy_setting)" href="#"><span>Enable/Disable</span> <div><a onclick="return enable_privacy(!current_privacy_setting)" href="#"><span>Enable/Disable</span>
New-Version Check</a></div> New-Version Check</a></div>
<p style="line-height: 125%"><small>(When enabled, status checks phone-home to check for a new release of <p style="line-height: 125%"><small>(When enabled, status checks phone-home to check for a new release of
Mail-in-a-Box.)</small></p> Mail-in-a-Box.)</small></p>
</div> </div>
</div> <!-- /col --> </div> <!-- /col -->
<div class="col-md-pull-3 col-md-8"> <div class="col-md-pull-3 col-md-8">
<table id="system-checks" class="table" style="max-width: 60em"> <table id="system-checks" class="table" style="max-width: 60em">
<thead> <thead>
</thead> </thead>
<tbody> <tbody>
</tbody> </tbody>
</table> </table>
</div> <!-- /col --> </div> <!-- /col -->
</div> <!-- /row --> </div> <!-- /row -->
<script> <script>
function show_system_status() { function show_system_status() {
$('#system-checks tbody').html("<tr><td colspan='2' class='text-muted'>Loading...</td></tr>") $('#system-checks tbody').html("<tr><td colspan='2' class='text-muted'>Loading...</td></tr>")
api( api(
"/system/privacy", "/system/privacy",
"GET", "GET",
{}, {},
function (r) { function (r) {
current_privacy_setting = r; current_privacy_setting = r;
$('#system-privacy-setting').show(); $('#system-privacy-setting').show();
$('#system-privacy-setting a span').text(r ? "Enable" : "Disable"); $('#system-privacy-setting a span').text(r ? "Enable" : "Disable");
$('#system-privacy-setting p').toggle(r); $('#system-privacy-setting p').toggle(r);
}); });
api( api(
"/system/reboot", "/system/reboot",
"GET", "GET",
{}, {},
function (r) { function (r) {
$('#system-reboot-required').show(); // show when r becomes available $('#system-reboot-required').show(); // show when r becomes available
$('#system-reboot-required').find('button').toggle(r); $('#system-reboot-required').find('button').toggle(r);
$('#system-reboot-required').find('div').toggle(!r); $('#system-reboot-required').find('div').toggle(!r);
}); });
api( api(
"/system/status", "/system/status",
"POST", "POST",
{}, {},
function (r) { function (r) {
$('#system-checks tbody').html(""); $('#system-checks tbody').html("");
for (var i = 0; i < r.length; i++) { for (var i = 0; i < r.length; i++) {
var n = $("<tr><td class='status'/><td class='message'><p style='margin: 0'/><div class='extra'/><a class='showhide' href='#'/></tr>"); var n = $("<tr><td class='status'/><td class='message'><p style='margin: 0'/><div class='extra'/><a class='showhide' href='#'/></tr>");
if (i == 0) n.addClass('first') if (i == 0) n.addClass('first')
if (r[i].type == "heading") if (r[i].type == "heading")
n.addClass(r[i].type) n.addClass(r[i].type)
else else
n.addClass("status-" + r[i].type) n.addClass("status-" + r[i].type)
if (r[i].type == "ok") n.find('td.status').text("✓") if (r[i].type == "ok") n.find('td.status').text("✓")
if (r[i].type == "error") n.find('td.status').text("✖") if (r[i].type == "error") n.find('td.status').text("✖")
if (r[i].type == "warning") n.find('td.status').text("?") if (r[i].type == "warning") n.find('td.status').text("?")
n.find('td.message p').text(r[i].text) n.find('td.message p').text(r[i].text)
$('#system-checks tbody').append(n); $('#system-checks tbody').append(n);
if (r[i].extra.length > 0) { if (r[i].extra.length > 0) {
n.find('a.showhide').show().text("show more").click(function () { n.find('a.showhide').show().text("show more").click(function () {
$(this).hide(); $(this).hide();
$(this).parent().find('.extra').fadeIn(); $(this).parent().find('.extra').fadeIn();
return false; return false;
}); });
} }
for (var j = 0; j < r[i].extra.length; j++) { for (var j = 0; j < r[i].extra.length; j++) {
var m = $("<div/>").text(r[i].extra[j].text) var m = $("<div/>").text(r[i].extra[j].text)
if (r[i].extra[j].monospace) if (r[i].extra[j].monospace)
m.addClass("pre"); m.addClass("pre");
n.find('> td.message > div').append(m); n.find('> td.message > div').append(m);
} }
} }
}) })
} }
var current_privacy_setting = null; var current_privacy_setting = null;
function enable_privacy(status) { function enable_privacy(status) {
api( api(
"/system/privacy", "/system/privacy",
"POST", "POST",
{ {
value: (status ? "private" : "off") value: (status ? "private" : "off")
}, },
function (res) { function (res) {
show_system_status(); show_system_status();
}); });
return false; // disable link return false; // disable link
} }
function confirm_reboot() { function confirm_reboot() {
show_modal_confirm( show_modal_confirm(
"Reboot", "Reboot",
$("<p>This will reboot your Mail-in-a-Box <code>{{hostname}}</code>.</p> <p>Until the machine is fully restarted, your users will not be able to send and receive email, and you will not be able to connect to this control panel or with SSH. The reboot cannot be cancelled.</p>"), $("<p>This will reboot your Mail-in-a-Box <code>{{hostname}}</code>.</p> <p>Until the machine is fully restarted, your users will not be able to send and receive email, and you will not be able to connect to this control panel or with SSH. The reboot cannot be cancelled.</p>"),
"Reboot Now", "Reboot Now",
function () { function () {
api( api(
"/system/reboot", "/system/reboot",
"POST", "POST",
{}, {},
function (r) { function (r) {
var msg = "<p>Please reload this page after a minute or so.</p>"; var msg = "<p>Please reload this page after a minute or so.</p>";
if (r) msg = "<p>The reboot command said:</p> <pre>" + $("<pre/>").text(r).html() + "</pre>"; // successful reboots don't produce any output; the output must be HTML-escaped if (r) msg = "<p>The reboot command said:</p> <pre>" + $("<pre/>").text(r).html() + "</pre>"; // successful reboots don't produce any output; the output must be HTML-escaped
show_modal_error("Reboot", msg); show_modal_error("Reboot", msg);
}); });
}); });
} }
</script> </script>