1
0
mirror of https://github.com/jhillyerd/inbucket.git synced 2025-12-18 01:57:02 +00:00

More bootstrap work, status page now updates

This commit is contained in:
James Hillyerd
2015-08-18 22:58:29 -07:00
parent da28a8ee55
commit 5623ac1e8e
7 changed files with 157 additions and 165 deletions

View File

@@ -1,3 +1,4 @@
/* Site Wide */
html { html {
position: relative; position: relative;
min-height: 100%; min-height: 100%;
@@ -9,11 +10,6 @@ body {
margin-bottom: 60px; margin-bottom: 60px;
} }
.mailbox-header {
text-align: center;
font-size: 18px;
}
.footer { .footer {
position: absolute; position: absolute;
bottom: 0; bottom: 0;
@@ -27,12 +23,14 @@ body {
margin: 20px 0; margin: 20px 0;
} }
.message-controls { /* Mailbox & Messages */
padding: 10px 0; .mailbox-header {
text-align: center;
font-size: 18px;
} }
#emailContent { .message-controls {
padding-bottom: 20px; padding: 0 0 10px 0;
} }
.message-header dl { .message-header dl {
@@ -49,31 +47,20 @@ body {
} }
} }
.message-body {
padding: 0 5px;
}
.message-attachments { .message-attachments {
margin-top: 20px; margin-top: 20px;
padding: 10px 20px;;
} }
.message-attachments ul { .message-attachments ul {
margin: 0 margin: 0
} }
#emailSubject { /* Metrics */
border-bottom: 1px #606060 solid;
margin: 0;
width: 617px;
}
#emailBody {
color: #555;
margin-top: 15px;
}
.errors {
background-color: #ffa0a0;
color: #333;
padding: 5px 10px;
}
table.metrics { table.metrics {
} }
@@ -90,27 +77,3 @@ table.metrics {
width: 200px; width: 200px;
} }
#emailAttachments {
border-collapse: collapse;
}
#emailAttachments th, #emailAttachments td {
text-align: left;
padding: 0 3px 3px 0;
}
#emailAttachments .fileName:before {
content: '\203A\00A0';
}
#emailAttachments a {
background: #8ac6dc;
color: #fff;
text-decoration: none;
padding: 0 5px;
}
#emailAttachments a:hover {
background: #becf74;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -27,8 +27,8 @@
</div> </div>
<div id="navbar" class="collapse navbar-collapse"> <div id="navbar" class="collapse navbar-collapse">
<ul class="nav navbar-nav"> <ul class="nav navbar-nav">
<li class="active"><a href="/" accesskey="1">Home</a></li> <li id="nav-mail"><a href="/" accesskey="1">Mail</a></li>
<li><a href="/status" accesskey="2">Status</a></li> <li id="nav-status"><a href="/status" accesskey="2">Status</a></li>
</ul> </ul>
<form class="navbar-form navbar-right" action="{{reverse "MailboxIndex"}}" method="GET"> <form class="navbar-form navbar-right" action="{{reverse "MailboxIndex"}}" method="GET">
<div class="form-group"> <div class="form-group">

View File

@@ -2,26 +2,26 @@
{{$id := .message.Id}} {{$id := .message.Id}}
<div class="btn-group btn-group-sm message-controls" role="group" aria-label="Message Controls"> <div class="btn-group btn-group-sm message-controls" role="group" aria-label="Message Controls">
<button type="button" <button type="button"
class="btn btn-default" class="btn btn-primary"
onClick="window.open('/link/{{$name}}/{{$id}}');"> onClick="window.open('/link/{{$name}}/{{$id}}');">
<span class="glyphicon glyphicon-link" aria-hidden="true"></span> <span class="glyphicon glyphicon-link" aria-hidden="true"></span>
Link Link
</button> </button>
<button type="button" <button type="button"
class="btn btn-default" class="btn btn-danger"
onClick="deleteMessage('{{.message.Id}}');"> onClick="deleteMessage('{{.message.Id}}');">
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> <span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
Delete Delete
</button> </button>
<button type="button" <button type="button"
class="btn btn-default" class="btn btn-primary"
onClick="messageSource('{{.message.Id}}');"> onClick="messageSource('{{.message.Id}}');">
<span class="glyphicon glyphicon-search" aria-hidden="true"></span> <span class="glyphicon glyphicon-search" aria-hidden="true"></span>
Source Source
</button> </button>
{{if .htmlAvailable}} {{if .htmlAvailable}}
<button type="button" <button type="button"
class="btn btn-default" class="btn btn-primary"
onClick="htmlView('{{.message.Id}}');"> onClick="htmlView('{{.message.Id}}');">
<span class="glyphicon glyphicon-new-window" aria-hidden="true"></span> <span class="glyphicon glyphicon-new-window" aria-hidden="true"></span>
HTML HTML
@@ -29,32 +29,46 @@
{{end}} {{end}}
</div> </div>
<div class="well well-sm message-header"> <div class="panel panel-default message-header">
<dl class="dl-horizontal">
<dt>From:</dt>
<dd>{{.message.From}}</dd>
<dt>Date:</dt>
<dd>{{.message.Date}}</dd>
<dt>Subject:</dt>
<dd>{{.message.Subject}}</dd>
</dl>
</div>
<div id="message-body">{{.body}}</div>
{{with .attachments}}
<div class="panel panel-default message-attachments">
<div class="panel-body"> <div class="panel-body">
<ul class="list-unstyled"> <dl class="dl-horizontal">
{{range $i, $e := .}} <dt>From:</dt>
<li> <dd>{{.message.From}}</dd>
{{$e.FileName}} <dt>Date:</dt>
({{$e.ContentType}}) <dd>{{.message.Date}}</dd>
<a href="/mailbox/vattach/{{$name}}/{{$id}}/{{$i}}/{{$e.FileName}}" target="_blank">View</a> <dt>Subject:</dt>
<a href="/mailbox/dattach/{{$name}}/{{$id}}/{{$i}}/{{$e.FileName}}">Download</a> <dd>{{.message.Subject}}</dd>
</li> </dl>
{{end}}
</ul>
</div> </div>
</div> </div>
<div class="message-body">{{.body}}</div>
{{with .attachments}}
<div class="well message-attachments">
<ul class="list-unstyled">
{{range $i, $e := .}}
<li>
<span class="glyphicon glyphicon-paperclip" aria-hidden="true"></span>
{{$e.FileName}}
({{$e.ContentType}})
<a class="btn btn-success btn-sm"
role="button"
href="/mailbox/vattach/{{$name}}/{{$id}}/{{$i}}/{{$e.FileName}}"
target="_blank"
aria-label="View">
<span class="glyphicon glyphicon-eye-open" aria-hidden="true"></span>
View
</a>
<a class="btn btn-primary btn-sm"
role="button"
hhref="/mailbox/dattach/{{$name}}/{{$id}}/{{$i}}/{{$e.FileName}}"
aria-label="Download">
<span class="glyphicon glyphicon-download" aria-hidden="true"></span>
Download
</a>
</li>
{{end}}
</ul>
</div>
{{end}} {{end}}

View File

@@ -1,49 +1,50 @@
{{define "title"}}{{printf "Inbucket for %v" .name}}{{end}} {{define "title"}}{{printf "Inbucket for %v" .name}}{{end}}
{{define "navMail"}}true{{end}}
{{$name := .name}} {{$name := .name}}
{{define "script"}} {{define "script"}}
<script> <script>
var selected = "{{.selected}}" var selected = "{{.selected}}";
function messageLoaded(responseText, textStatus, XMLHttpRequest) { function messageLoaded(responseText, textStatus, XMLHttpRequest) {
if (textStatus == "error") { if (textStatus == "error") {
alert("Failed to load message, server said:\n" + responseText) alert("Failed to load message, server said:\n" + responseText);
} else { } else {
window.scrollTo(0,0) window.scrollTo(0,0);
} }
} }
function listLoaded() { function listLoaded() {
$('.listEntry').click( $('.listEntry').click(
function() { function() {
$('.listEntry').removeClass("disabled") $('.listEntry').removeClass("disabled");
$(this).addClass("disabled") $(this).addClass("disabled");
$('#emailContent').load('/mailbox/{{.name}}/' + this.id, messageLoaded) $('#emailContent').load('/mailbox/{{.name}}/' + this.id, messageLoaded);
selected = this.id selected = this.id;
} }
) )
$("#messageList").slideDown() $("#messageList").slideDown();
if (selected != "") { if (selected != "") {
$("#" + selected).click() $("#" + selected).click();
selected = "" selected = "";
} }
} }
function loadList() { function loadList() {
$('#messageList').load("/mailbox/{{.name}}", listLoaded) $('#messageList').load("/mailbox/{{.name}}", listLoaded);
} }
function reloadList() { function reloadList() {
$('#messageList').hide() $('#messageList').hide();
loadList() loadList();
} }
function listInit() { function listInit() {
$("#messageList").hide() $("#messageList").hide();
loadList() loadList();
} }
function deleteMessage(id) { function deleteMessage(id) {
$('#emailContent').empty() $('#emailContent').empty();
$.ajax({ $.ajax({
type: 'DELETE', type: 'DELETE',
url: '/mailbox/{{.name}}/' + id, url: '/mailbox/{{.name}}/' + id,
@@ -53,22 +54,27 @@
function htmlView(id) { function htmlView(id) {
window.open('/mailbox/{{.name}}/' + id + "/html", '_blank', window.open('/mailbox/{{.name}}/' + id + "/html", '_blank',
'width=800,height=600,' + 'width=800,height=600,' +
'menubar=yes,resizable=yes,scrollbars=yes,status=yes,toolbar=yes') 'menubar=yes,resizable=yes,scrollbars=yes,status=yes,toolbar=yes');
} }
function messageSource(id) { function messageSource(id) {
window.open('/mailbox/{{.name}}/' + id + "/source", '_blank', window.open('/mailbox/{{.name}}/' + id + "/source", '_blank',
'width=800,height=600,' + 'width=800,height=600,' +
'menubar=no,resizable=yes,scrollbars=yes,status=no,toolbar=no') 'menubar=no,resizable=yes,scrollbars=yes,status=no,toolbar=no');
} }
$(document).ready(listInit) function docReady() {
$('#nav-mail').addClass("active");
listInit();
}
$(document).ready(docReady)
</script> </script>
{{end}} {{end}}
{{define "content"}} {{define "content"}}
<div class="panel panel-primary"> <div class="panel panel-info">
<div class="panel-heading mailbox-header"> <div class="panel-heading mailbox-header">
<span class="glyphicon glyphicon-inbox" aria-hidden="true"></span> <span class="glyphicon glyphicon-inbox" aria-hidden="true"></span>
{{.name}} {{.name}}

View File

@@ -2,32 +2,33 @@
{{define "script"}} {{define "script"}}
<script src="/public/jquery.sparkline.min.js" type="text/javascript" charset="utf-8"></script> <script src="/public/jquery.sparkline.min.js" type="text/javascript" charset="utf-8"></script>
<script src="/public/jquery.color-2.1.2.min.js" type="text/javascript" charset="utf-8"></script>
<script> <script>
jQuery.ajaxSetup({ cache: false }) jQuery.ajaxSetup({ cache: false });
flashOn = jQuery.Color("rgba(255,255,0,1)") flashOn = jQuery.Color("rgba(255,255,0,1)");
flashOff = jQuery.Color("rgba(255,255,0,0)") flashOff = jQuery.Color("rgba(255,255,0,0)");
dataHist = new Object() dataHist = new Object();
function timeFilter(seconds) { function timeFilter(seconds) {
if (seconds < 60) { if (seconds < 60) {
return seconds + " seconds" return seconds + " seconds";
} else if (seconds < 3600) { } else if (seconds < 3600) {
return (seconds/60).toFixed(0) + " minute(s)" return (seconds/60).toFixed(0) + " minute(s)";
} else if (seconds < 86400) { } else if (seconds < 86400) {
return (seconds/3600).toFixed(1) + " hour(s)" return (seconds/3600).toFixed(1) + " hour(s)";
} }
return (seconds/86400).toFixed(0) + " day(s)" return (seconds/86400).toFixed(0) + " day(s)";
} }
function sizeFilter(bytes) { function sizeFilter(bytes) {
if (bytes < 1024) { if (bytes < 1024) {
return bytes + " bytes" return bytes + " bytes";
} else if (bytes < 1048576) { } else if (bytes < 1048576) {
return (bytes/1024).toFixed(0) + " KB" return (bytes/1024).toFixed(0) + " KB";
} else if (bytes < 1073741824) { } else if (bytes < 1073741824) {
return (bytes/1048576).toFixed(2) + " MB" return (bytes/1048576).toFixed(2) + " MB";
} }
return (bytes/1073741824).toFixed(2) + " GB" return (bytes/1073741824).toFixed(2) + " GB";
} }
function numberFilter(x) { function numberFilter(x) {
@@ -37,101 +38,102 @@
} }
function appendHistory(name, value) { function appendHistory(name, value) {
var h = dataHist[name] var h = dataHist[name];
if (! h) { if (! h) {
h = new Array(0) h = new Array(0);
} }
// Prevent array from growing // Prevent array from growing
if (h.length >= 60) { if (h.length >= 60) {
h = h.slice(1,60) h = h.slice(1,60);
} }
h.push(parseInt(value)) h.push(parseInt(value));
dataHist[name] = h dataHist[name] = h;
el = $('#s-' + name) el = $('#s-' + name);
if (el) { if (el) {
el.sparkline(dataHist[name]) el.sparkline(dataHist[name]);
} }
} }
// Show spikes for numbers that only increase // Show spikes for numbers that only increase
function setHistoryOfActivity(name, value) { function setHistoryOfActivity(name, value) {
var h = value.split(",") var h = value.split(",");
var prev = parseInt(h[0]) var prev = parseInt(h[0]);
for (i=0; i<h.length; i++) { for (i=0; i<h.length; i++) {
var t = parseInt(h[i]) var t = parseInt(h[i]);
h[i] = t-prev h[i] = t-prev;
prev = t prev = t;
} }
// First value will always be zero // First value will always be zero
if (h.length > 0) { if (h.length > 0) {
h = h.slice(1) h = h.slice(1);
} }
el = $('#s-' + name) el = $('#s-' + name);
if (el) { if (el) {
el.sparkline(h) el.sparkline(h);
} }
} }
// Show up/down for numbers that can decrease // Show up/down for numbers that can decrease
function setHistoryOfCount(name, value) { function setHistoryOfCount(name, value) {
var h = value.split(",") var h = value.split(",");
el = $('#s-' + name) el = $('#s-' + name);
if (el) { if (el) {
el.sparkline(h) el.sparkline(h);
} }
} }
function metric(name, value, filter, chartable) { function metric(name, value, filter, chartable) {
if (chartable) { if (chartable) {
appendHistory(name, value) appendHistory(name, value);
} }
if (filter) { if (filter) {
value = filter(value) value = filter(value);
} }
var el = $('#m-' + name) var el = $('#m-' + name)
if (el.text() != value) { if (el.text() != value) {
el.text(value) el.text(value);
el.css('background-color', flashOn) el.css('background-color', flashOn);
el.animate({ backgroundColor: flashOff }, 1500) el.animate({ backgroundColor: flashOff }, 1500);
} }
} }
function displayMetrics(data, textStatus, jqXHR) { function displayMetrics(data, textStatus, jqXHR) {
// Non graphing // Non graphing
metric('uptime', data.uptime, timeFilter, false) metric('uptime', data.uptime, timeFilter, false);
metric('retentionScanCompleted', data.retention.SecondsSinceScanCompleted, timeFilter, false) metric('retentionScanCompleted', data.retention.SecondsSinceScanCompleted, timeFilter, false);
metric('retentionPeriod', data.retention.Period, timeFilter, false) metric('retentionPeriod', data.retention.Period, timeFilter, false);
// JavaScript history // JavaScript history
metric('memstatsSys', data.memstats.Sys, sizeFilter, true) metric('memstatsSys', data.memstats.Sys, sizeFilter, true);
metric('memstatsHeapAlloc', data.memstats.HeapAlloc, sizeFilter, true) metric('memstatsHeapAlloc', data.memstats.HeapAlloc, sizeFilter, true);
metric('memstatsHeapSys', data.memstats.HeapSys, sizeFilter, true) metric('memstatsHeapSys', data.memstats.HeapSys, sizeFilter, true);
metric('memstatsHeapObjects', data.memstats.HeapObjects, numberFilter, true) metric('memstatsHeapObjects', data.memstats.HeapObjects, numberFilter, true);
metric('smtpConnectsCurrent', data.smtp.ConnectsCurrent, numberFilter, true) metric('smtpConnectsCurrent', data.smtp.ConnectsCurrent, numberFilter, true);
// Server-side history // Server-side history
metric('smtpReceivedTotal', data.smtp.ReceivedTotal, numberFilter, false) metric('smtpReceivedTotal', data.smtp.ReceivedTotal, numberFilter, false);
setHistoryOfActivity('smtpReceivedTotal', data.smtp.ReceivedHist) setHistoryOfActivity('smtpReceivedTotal', data.smtp.ReceivedHist);
metric('smtpConnectsTotal', data.smtp.ConnectsTotal, numberFilter, false) metric('smtpConnectsTotal', data.smtp.ConnectsTotal, numberFilter, false);
setHistoryOfActivity('smtpConnectsTotal', data.smtp.ConnectsHist) setHistoryOfActivity('smtpConnectsTotal', data.smtp.ConnectsHist);
metric('smtpWarnsTotal', data.smtp.WarnsTotal, numberFilter, false) metric('smtpWarnsTotal', data.smtp.WarnsTotal, numberFilter, false);
setHistoryOfActivity('smtpWarnsTotal', data.smtp.WarnsHist) setHistoryOfActivity('smtpWarnsTotal', data.smtp.WarnsHist);
metric('smtpErrorsTotal', data.smtp.ErrorsTotal, numberFilter, false) metric('smtpErrorsTotal', data.smtp.ErrorsTotal, numberFilter, false);
setHistoryOfActivity('smtpErrorsTotal', data.smtp.ErrorsHist) setHistoryOfActivity('smtpErrorsTotal', data.smtp.ErrorsHist);
metric('retentionDeletesTotal', data.retention.DeletesTotal, numberFilter, false) metric('retentionDeletesTotal', data.retention.DeletesTotal, numberFilter, false);
setHistoryOfActivity('retentionDeletesTotal', data.retention.DeletesHist) setHistoryOfActivity('retentionDeletesTotal', data.retention.DeletesHist);
metric('retainedCurrent', data.retention.RetainedCurrent, numberFilter, false) metric('retainedCurrent', data.retention.RetainedCurrent, numberFilter, false);
setHistoryOfCount('retainedCurrent', data.retention.RetainedHist) setHistoryOfCount('retainedCurrent', data.retention.RetainedHist);
} }
function loadMetrics() { function loadMetrics() {
// jQuery.getJSON( url [, data] [, success(data, textStatus, jqXHR)] ) // jQuery.getJSON( url [, data] [, success(data, textStatus, jqXHR)] )
jQuery.getJSON('/debug/vars', null, displayMetrics) jQuery.getJSON('/debug/vars', null, displayMetrics);
} }
function aboutInit() { function aboutInit() {
loadMetrics() $('#navStatus').addClass('active');
setInterval(loadMetrics, 10000) loadMetrics();
setInterval(loadMetrics, 10000);
} }
$(document).ready(aboutInit) $(document).ready(aboutInit)