Browse Source

minerpond init template

pull/1/head
webworker01 5 years ago
parent
commit
d40875c8ad
  1. 86
      website/minerpond/index.html
  2. 2798
      website/minerpond/key.html
  3. 50
      website/minerpond/pages/admin.html
  4. 11
      website/minerpond/pages/api.html
  5. 325
      website/minerpond/pages/getting_started.html
  6. 134
      website/minerpond/pages/home.html
  7. 105
      website/minerpond/pages/miner_stats.html
  8. 25
      website/minerpond/pages/mining_key.html
  9. 94
      website/minerpond/pages/payments.html
  10. 360
      website/minerpond/pages/stats.html
  11. 64
      website/minerpond/pages/tbs.html
  12. 94
      website/minerpond/pages/workers.html
  13. 100
      website/minerpond/static/admin.js
  14. BIN
      website/minerpond/static/favicon.png
  15. 13
      website/minerpond/static/kmdfavicon.svg
  16. BIN
      website/minerpond/static/komodo-logo-horizontal-01.png
  17. 13
      website/minerpond/static/logo.svg
  18. 30
      website/minerpond/static/main.js
  19. 246
      website/minerpond/static/miner_stats.js
  20. 1
      website/minerpond/static/nvd3.css
  21. 6
      website/minerpond/static/nvd3.js
  22. 143
      website/minerpond/static/stats.js
  23. 68
      website/minerpond/static/style.css

86
website/minerpond/index.html

@ -0,0 +1,86 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="shortcut icon" type="image/png" href="/static/kmdfavicon.svg"/>
<link href='http://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.0.3/css/font-awesome.min.css">
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/normalize/3.0.1/normalize.min.css">
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/pure/0.4.2/pure-min.css">
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.4.5/d3.min.js"></script>
<script src="/static/nvd3.js"></script>
<link rel="stylesheet" href="/static/nvd3.css">
<script src="/static/main.js"></script>
<link rel="stylesheet" href="/static/style.css">
<title>minerpond - komodo assetchains mining pool</title>
</head>
<body>
<header>
<div class="home-menu pure-menu pure-menu-open pure-menu-horizontal">
<a class="pure-menu-heading hot-swapper" href="/"><i class="fa fa-home"></i>&nbsp;Home</a>
<ul>
<li class="{{? it.selected === 'getting_started' }}pure-menu-selected{{?}}">
<a class="hot-swapper" href="/getting_started">
<i class="fa fa-rocket"></i>&nbsp;
Getting Started
</a>
</li>
<li class="{{? it.selected === 'stats' }}pure-menu-selected{{?}}">
<a class="hot-swapper" href="/stats">
<i class="fa fa-bar-chart-o"></i>&nbsp;
Graph Stats
</a>
</li>
<li class="{{? it.selected === 'tbs' }}pure-menu-selected{{?}}">
<a class="hot-swapper" href="/tbs">
<i class="fa fa-table"></i>&nbsp;
Tab Stats
</a>
</li>
<li class="{{? it.selected === 'workers' }}pure-menu-selected{{?}}">
<a class="hot-swapper" href="/workers">
<i class="fa fa-cogs"></i>&nbsp;
Workers Stats
</a>
</li>
<li class="{{? it.selected === 'payments' }}pure-menu-selected{{?}}">
<a class="hot-swapper" href="/payments">
<i class="fa fa-bitcoin"></i>&nbsp;
Payments
</a>
</li>
<li class="{{? it.selected === 'api' }}pure-menu-selected{{?}}">
<a class="hot-swapper" href="/api">
<i class="fa fa-code"></i>&nbsp;
API
</a>
</li>
</ul>
</div>
</header>
<main>
{{=it.page}}
</main>
<footer>
&lt;3 <a href="https://webworker.sh" target="_blank">🐸</a> &middot; <a href="https://github.com/webworker01/knomp" target="_blank">knomp on Github</a> &middot; <a href="https://discord.gg/QaPwxKB" target="_blank" rel="nofollow noreferrer noopener">Discord Chat</a>
</footer>
</body>
</html>

2798
website/minerpond/key.html

File diff suppressed because it is too large

50
website/minerpond/pages/admin.html

@ -0,0 +1,50 @@
<div>
<style>
#passwordForm, #adminCenter{
display: none;
}
#adminCenter{
display: flex;
flex-flow: row;
}
#leftMenu{
flex: 0 0 200px;
}
#editForm{
flex: 1 1 auto;
}
</style>
<form class="pure-form pure-form-stacked" id="passwordForm">
<fieldset>
<legend>Password</legend>
<input id="password" type="password" placeholder="Password">
<label for="remember" class="pure-checkbox">
<input id="remember" type="checkbox"> Stay Logged In
</label>
<button type="submit" class="pure-button pure-button-primary">Log In</button>
</fieldset>
</form>
<div id="adminCenter">
<div class="pure-menu pure-menu-open" id="leftMenu">
<a class="pure-menu-heading">Administration</a>
<ul>
<li id="addPool"><a href="#">Add Pool</a></li>
<li class="pure-menu-heading" id="poolList">Current Pools</li>
</ul>
</div>
<div id="editForm"></div>
</div>
<script src="/static/admin.js"></script>
</div>

11
website/minerpond/pages/api.html

@ -0,0 +1,11 @@
<div style="margin: 18px;">
API - The API is work in progress and is subject to change during development.
<ul>
<li><a href="/api/stats">/api/stats</a> global pool stats</li>
<li><a href="/api/blocks">/api/blocks</a> global block stats</li>
<li><a href="/api/pool_stats">/api/pool_stats</a> - historical stats</li>
<li><a href="/api/payments">/api/payments</a> - payment history</li>
<li><a href="/api/worker_stats?taddr">/api/worker_stats?taddr</a> - historical time per pool json </li>
<li><a href="/api/live_stats">/api/live_stats</a> - live stats (websocket)</li>
</ul>
</div>

325
website/minerpond/pages/getting_started.html

@ -0,0 +1,325 @@
<style>
#holder{
display: flex;
flex-direction: row;
}
.glow{
box-shadow: inset 0 0 12px 4px #ff6c00;
}
.hidden{
display: none !important;
}
#menu{
background-color: #3d3d3d;
min-width: 170px;
}
#menu > .menuHeader{
color: #e3f7ff;
border-bottom: 1px solid #7f878b;
font-size: 1.2em;
padding: 16px 16px 4px 15px;
}
.menuList{
transition-duration: 200ms;
}
.menuList > a:first-child{
margin-top: 10px;
}
.menuList > a{
display: inline-block;
color: #e3f7ff;
text-decoration: none;
padding: 7px;
padding-left: 25px;
width:40px;
}
.menuList > a:hover{
color: #f69b3a;
}
#main{
flex: 1 1 auto;
display: flex;
flex-direction: column;
margin: 18px;
}
.miningOption{
color: white;
/* display: flex;
flex: 1 1 auto;
flex-direction: row;
flex-wrap: wrap; */
min-height: 215px;
justify-content: center;
align-items: center;
text-decoration: none;
}
a.miningOption:hover{
color: #f69b3a;
}
.miningOption:first-child{
background-color: #0eafc7;
}
.miningOption:last-child{
background-color: #b064e1;
}
.miningOptionNum{
font-size: 6em;
padding-right: 20px;
width: 140px;
text-align: center;
}
.miningOptionInstructions{
flex: 1 1 auto;
padding:10px;
}
.miningOptionInstructions > div:first-child{
font-size: 2.4em;
}
.miningOptionInstructions > div:last-child{
margin-top: 20px;
font-size: 1.3em;
}
#orHolder{
height: 37px;
text-align: center;
}
#orLine{
border-bottom: 1px solid #c2cacf;
height: 19px;
margin-bottom: -13px;
}
#orText{
background-color: #ebf4fa;
color: #5c5c5c;
display: inline-block;
width: 35px;
font-style: italic;
}
#coinInfoBackground{
transition-duration: 400ms;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: black;
opacity: 0.0;
}
#coinInfo{
display: flex;
flex-direction: column;
color: white;
width: 750px;
min-height: 400px;
top: 50px;
left: 50%;
margin-left: -375px;
position: absolute;
background-color: #f06350;
}
#coinInfo .coinInfoName{
text-transform: capitalize;
}
#coinInfo > div:first-of-type{
font-size: 1.8em;
text-align: center;
margin-top: 40px;
margin-bottom: 35px;
}
#coinInfoRows{
display: flex;
flex-direction: row;
justify-content: center;
flex: 1 1 auto;
margin-bottom: 70px;
}
#coinInfoRows > div{
display: flex;
flex-direction: column;
justify-content: center;
}
#coinInfoRows > div > div{
padding: 3px;
}
#coinInfoRowKeys{
font-weight: bold;
padding-right: 30px;
}
#coinInfoRowKeys .coinInfoSubtle{
font-weight: normal;
}
#coinInfoRowValues{
}
#coinInfoClose{
position: absolute;
font-size: 3em;
top: 0;
right: 0;
width: 60px;
height: 60px;
text-align: center;
color: white;
text-decoration: none;
}
#coinInfoClose:hover{
color: #50f0e3;
}
</style>
<div id="holder">
<a href="#" class="miningOption" id="coinGlowTrigger">
<div class="miningOptionInstructions">
<div>Select a coin for connection details</div>
<div>Configurations for each coin are available for advanced miners</div>
</div>
</a>
<div id="menu">
{{? (function(){
if (!it.portalConfig.switching) return false;
for (var p in it.portalConfig.switching){
if (it.portalConfig.switching[p].enabled)
return true;
}
return false;
})()
}}
<div class="menuHeader">Coin-Switching Ports</div>
{{?}}
<div class="menuList">
{{ for (var p in it.portalConfig.switching){
if (!it.portalConfig.switching[p].enabled) continue;
var info = {
algo: p,
ports: {},
host: it.portalConfig.website.stratumHost
};
info.ports[it.portalConfig.switching[p].port] = {diff: it.portalConfig.switching[p].diff};
info = JSON.stringify(info).replace(/"/g, '&quot;');
}}
<a href="#" class="poolOption" data-info="{{=info}}">{{=p}}</a>
{{ } }}
</div>
<div class="menuHeader">Coins</div>
<div class="menuList" id="coinList">
{{ for(var pool in it.poolsConfigs) {
var info = JSON.stringify({
coin: it.poolsConfigs[pool].coin,
algo: it.poolsConfigs[pool].coin.algorithm,
ports: it.poolsConfigs[pool].ports,
host: it.portalConfig.website.stratumHost
}).replace(/"/g, '&quot;');
}}
<a href="#" class="poolOption" data-info="{{=info}}">{{=pool}}</a>
{{ } }}
</div>
</div>
<!-- <div id="main">
<a href="#" class="miningOption" id="nompAppDownload">
<div class="miningOptionNum">1.</div>
<div class="miningOptionInstructions">
<div>Download NOMP App</div>
<div>Our preconfigured app makes mining that easy</div>
</div>
</a>
<div id="orHolder">
<div id="orLine"></div>
<div id="orText">or</div>
</div>
<a href="#" class="miningOption" id="coinGlowTrigger">
<div class="miningOptionNum">2.</div>
<div class="miningOptionInstructions">
<div>Select a coin for connection details</div>
<div>Configurations for each coin are available for advanced miners</div>
</div>
</a>
</div> -->
</div>
<a href="#" id="coinInfoBackground" class="hidden"></a>
<div id="coinInfo" class="hidden">
<a href="#" id="coinInfoClose">×</a>
<div><span class="coinInfoName"></span> Configuration:</div>
<div id="coinInfoRows">
<div id="coinInfoRowKeys">
<div>Username:</div>
<div>Password:</div>
</div>
<div id="coinInfoRowValues">
<div id="coinInfoUsername"></div>
<div>anything</div>
</div>
</div>
</div>
<script>
function showCoinConfig(info){
var htmlKeys = '<div class="coinInfoData">Algorithm:</div>';
var htmlValues = '<div class="coinInfoData">' + info.algo + '</div>';
for (var port in info.ports){
htmlKeys += '<div class="coinInfoData">URL <span class="coinInfoSubtle">(difficulty ' + info.ports[port].diff + ')</span>:</div>';
htmlValues += '<div class="coinInfoData">stratum+tcp://' + info.host + ':' + port + '</div>';
}
if (info.coin)
$('#coinInfoUsername').text('your ' + info.coin.name + ' wallet address');
else
$('#coinInfoUsername').text('your public key');
$('.coinInfoData').remove();
$('#coinInfoRowKeys').append(htmlKeys);
$('#coinInfoRowValues').append(htmlValues);
}
$('#coinGlowTrigger').click(function(event){
event.preventDefault();
$('.menuList').addClass('glow');
setTimeout(function(){
$('.menuList').removeClass('glow');
}, 200);
return false;
});
$('.poolOption').click(function(event){
event.preventDefault();
showCoinConfig($(this).data('info'));
$('#coinInfoBackground,#coinInfo').removeClass('hidden');
$('#coinInfoBackground').css('opacity', 0.7);
return false;
});
$('#coinInfoBackground,#coinInfoClose').click(function(event){
event.preventDefault();
$('#coinInfoBackground,#coinInfo').addClass('hidden');
$('#coinInfoBackground').css('opacity', 0.0);
return false;
});
$('#nompAppDownload').click(function(event){
event.preventDefault();
alert('NOMP App development still in progress...');
return false;
});
</script>

134
website/minerpond/pages/home.html

@ -0,0 +1,134 @@
<style>
#boxWelcome{
background-color: #0eafc7;
color: white;
margin: 18px;
}
#logoImg{
height: 285px;
margin: 55px;
}
#welcomeText{
font-size: 2.7em;
margin: 50px 18px 10px 18px;
}
#welcomeItems{
list-style-type: none;
font-size: 1.3em;
padding: 0 !important;
margin: 0 0 0 18px !important;
}
#welcomeItems > li{
margin: 30px !important;
}
#boxesLower {
margin: 0 9px;
}
#boxesLower > div {
display: flex;
}
#boxesLower > div > div {
flex: 1 1 auto;
margin: 0 9px 18px 9px;
padding: 10px;
display: flex;
flex-direction: column;
}
.boxLowerHeader{
font-size: 1.3em;
margin: 0 0 5px 10px;
}
#boxStatsLeft{
background-color: #b064e1;
}
#boxStatsRight{
background-color: #10bb9c;
}
.boxStats{
color: white;
}
.boxStatsList{
display: flex;
flex-flow: row wrap;
justify-content: space-around;
opacity: 0.77;
margin-bottom: 5px;
flex: 1 1 auto;
/* align-content: center; */
}
.boxStatsList i.fa{
height: 15px;
width: 33px;
text-align: center;
}
.boxStatsList > div{
padding: 5px 20px;
}
.boxStatsList > div > div{
padding: 3px;
}
</style>
<div class="pure-g-r" id="boxWelcome">
<!-- <img src="/static/komodo-logo-horizontal-01.png" style="margin: 0 auto; height:150px;"> -->
<div class="pure-u-1">
<div style="font-size: 2em; text-align:center; margin-bottom:10px;">MinerPond 🐸</div>
<div style="text-align:center; margin-bottom:10px;">Low 0.5% fee! Min payouts of 1!</div>
<div style="text-align:center; margin-bottom:10px;">KMDICE: Use a KMD compatible R address! Currently 15 minute payouts.</div>
<div style="text-align:center; margin-bottom:10px;">HUSH3Z: Use a zs1 sapling address! Currently 1 hour payouts.</div>
</div>
</div>
<div class="pure-g-r" id="boxesLower">
<div class="pure-u-1">
<div class="boxStats" id="boxStatsLeft">
<div class="boxLowerHeader">Global Stats</div>
<div class="boxStatsList">
{{ for(var algo in it.stats.algos) { }}
<div>
<div><i class="fa fa-flask"></i>{{=algo}}</div>
<div><i class="fa fa-users"></i><span id="statsMiners{{=algo}}">{{=it.stats.algos[algo].workers}}</span> Miners</div>
<div><i class="fa fa-tachometer"></i><span id="statsHashrate{{=algo}}">{{=it.stats.algos[algo].hashrateString}}</span></div>
</div>
{{ } }}
</div>
</div>
</div>
<div class="pure-u-1">
<div class="boxStats" id="boxStatsRight">
<div class="boxLowerHeader">Pools / Coins</div>
<div class="boxStatsList">
{{ for(var pool in it.stats.pools) { }}
<div>
<div><i class="fa fa-dot-circle-o"></i>{{=pool}}</div>
<div><i class="fa fa-users"></i><span id="statsMiners{{=pool}}">{{=it.stats.pools[pool].workerCount}}</span> Miners</div>
<div><i class="fa fa-tachometer"></i><span id="statsHashrate{{=pool}}">{{=it.stats.pools[pool].hashrateString}}</span></div>
</div>
{{ } }}
</div>
</div>
</div>
</div>
<script>
$(function() {
statsSource.addEventListener('message', function (e) {
var stats = JSON.parse(e.data);
for (algo in stats.algos) {
$('#statsMiners' + algo).text(stats.algos[algo].workers);
$('#statsHashrate' + algo).text(stats.algos[algo].hashrateString);
}
for (var pool in stats.pools) {
$('#statsMiners' + pool).text(stats.pools[pool].workerCount);
$('#statsHashrate' + pool).text(stats.pools[pool].hashrateString);
}
});
});
</script>

105
website/minerpond/pages/miner_stats.html

@ -0,0 +1,105 @@
<style>
#topCharts{
padding-left: 18px;
padding-right: 18px;
padding-top: 18px;
padding-bottom: 0px;
}
#topCharts > div > div > svg{
display: block;
height: 280px;
}
.chartWrapper{
border: solid 1px #c7c7c7;
border-radius: 5px;
padding: 5px;
margin-bottom: 18px;
}
.chartLabel{
font-size: 1.2em;
text-align: center;
padding: 4px;
}
.chartHolder{
}
#boxesWorkers {
margin: 0 9px;
}
#boxesWorkers > div {
display: flex;
}
#boxesWorkers > div > div {
flex: 1 1 auto;
margin: 0 9px 18px 9px;
padding: 10px;
display: flex;
flex-direction: column;
}
.boxLowerHeader{
font-size: 1.3em;
margin: 0 0 5px 10px;
}
#boxStatsLeft{
color: black;
background-color: #cccccc;
}
#boxStatsRight{
color: black;
background-color: #cccccc;
}
.boxStats{
color: white;
}
.boxStatsList{
display: flex;
flex-flow: row wrap;
justify-content: space-around;
opacity: 0.77;
margin-bottom: 5px;
flex: 1 1 auto;
align-content: center;
}
.boxStatsList i.fa{
height: 15px;
width: 33px;
text-align: center;
}
.boxStatsList > div{
padding: 5px 20px;
}
.boxStatsList > div > div{
padding: 3px;
}
</style>
<div id="topCharts">
<div class="chartWrapper">
<div class="chartLabel">
<!--<div style="float:left; padding-right: 18px;"><i class="fa fa-users"></i><span id="statsWorkers">...</span></div>-->
<div style="float:left; margin-right: 9px;">{{=String(it.stats.address).split(".")[0]}}</div>
<div style="float:right; padding-left: 18px;"><small><i class="fa fa-tachometer"></i> <span id="statsHashrateAvg">...</span> (Avg)</small></div>
<div style="float:right; padding-left: 18px;"><small><i class="fa fa-tachometer"></i> <span id="statsHashrate">...</span> (Now)</small></div>
<div style="float:right; padding-left: 18px;"><small><i class="fa fa-gavel"></i> Luck <span id="statsLuckDays">...</span> Days</small></div>
</div>
<div class="chartHolder"><svg id="workerHashrate" /></div>
<div>
<div style="float:right; padding-top: 9px; padding-right: 18px;"><i class="fa fa-cog"></i> Shares: <span id="statsTotalShares">...</span></div>
<div style="float:left; padding-top: 9px; padding-left: 18px; padding-right: 18px;"><i class="fa fa-money"></i> Immature: <span id="statsTotalImmature">...</span> </div>
<div style="float:left; padding-top: 9px; padding-left: 18px; padding-right: 18px;"><i class="fa fa-money"></i> Bal: <span id="statsTotalBal">...</span> </div>
<div style="padding-top: 9px; padding-left: 18px;"><i class="fa fa-money"></i> Paid: <span id="statsTotalPaid">...</span> </div>
</div>
</div>
</div>
<div id="boxesWorkers"> </div>
<script>
var _miner = "{{=String(it.stats.address).split(".")[0]}}";
var _workerCount = 0;
window.statsSource = new EventSource("/api/live_stats");
document.querySelector('main').appendChild(document.createElement('script')).src = '/static/miner_stats.js';
</script>

25
website/minerpond/pages/mining_key.html

@ -0,0 +1,25 @@
<style>
#miningKeyPage{
margin: 15px;
}
#keyFrame{
padding: 0;
border: 0;
width: 100%;
height: 750px;
display: block;
}
</style>
<div id="miningKeyPage">
<p>
This script run client-side (in your browser). For maximum security <a href="/key.html" download="key.html">download</a> the script and run it locally and
offline in a modern web browser.
</p>
<iframe id="keyFrame" src="/key.html">
</iframe>
</div>

94
website/minerpond/pages/payments.html

@ -0,0 +1,94 @@
<style>
#bottomNotes {
display: block;
padding-left: 18px;
padding-right: 18px;
padding-bottom: 18px;
}
#topPool {
padding-top: 18px;
padding-left: 18px;
padding-right: 18px;
}
#topPool > div > div > svg {
display: block;
height: 280px;
}
.poolWrapper {
border: solid 1px #c7c7c7;
border-radius: 5px;
padding: 5px;
margin-bottom: 18px;
}
.poolLabel {
font-size: 1.2em;
text-align: center;
padding: 4px;
}
.poolMinerTable {
}
table {
width: 100%;
}
table td {
padding: 6px 12px;
}
</style>
<script type="text/javascript">
$(function () {
$(document).tooltip({
content: function () {
return $(this).prop('title');
},
show: null,
close: function (event, ui) {
ui.tooltip.hover(
function () {
$(this).stop(true).fadeTo(400, 1);
},
function () {
$(this).fadeOut("400", function () {
$(this).remove();
})
});
}
});
});
</script>
{{ function readableDate(a){ return new Date(parseInt(a)).toISOString().substring(0, 16).replace('T', ' ') + ' UTC'; } }}
{{ for(var pool in it.stats.pools) { }}
<table class="puretable">
<thead>
<tr>
<th>Blocks</th>
<th>Time</th>
<th>Miners</th>
<th>Shares</th>
<th>Amount</th>
</tr>
</thead>
{{ for(var p in it.stats.pools[pool].payments) { }}
<tr>
<td style="max-width: 475px; word-wrap: break-word;">
{{if (String(it.stats.pools[pool].name) == 'kmd') { }}
<a href="https://kmdexplorer.io/tx/{{=it.stats.pools[pool].payments[p].txid}}" title="View transaction" target="_blank" rel="noopener noreferrer">{{=it.stats.pools[pool].payments[p].blocks}}</a>
{{ } else if (String(it.stats.pools[pool].name) == 'hush3z') { }}
<a href="https://hush.explorer.dexstats.info/tx/{{=it.stats.pools[pool].payments[p].txid}}" target="_blank" rel="noopener noreferrer">{{=it.stats.pools[pool].payments[p].blocks}}</a>
{{ } else if (String(it.stats.pools[pool].name) == 'kmdice') { }}
<a href="http://kmdice.explorer.dexstats.info/tx/{{=it.stats.pools[pool].payments[p].txid}}" target="_blank" rel="noopener noreferrer">{{=it.stats.pools[pool].payments[p].blocks}}</a>
{{ } else { }}
{{=it.stats.pools[pool].payments[p].blocks}}
{{ } }}
</td>
<td>{{=readableDate(it.stats.pools[pool].payments[p].time)}}</td>
<td>{{=it.stats.pools[pool].payments[p].miners}}</td>
<td>{{=Math.round(it.stats.pools[pool].payments[p].shares)}}</td>
<td>{{=it.stats.pools[pool].payments[p].paid}} {{=it.stats.pools[pool].symbol}}</td>
</tr>
{{ } }}
</table>
</div>
{{ } }}

360
website/minerpond/pages/stats.html

@ -0,0 +1,360 @@
<style>
#topCharts{
padding: 18px;
}
#topCharts > div > div > svg{
display: block;
height: 280px;
}
.chartWrapper{
border: solid 1px #c7c7c7;
border-radius: 5px;
padding: 5px;
margin-bottom: 18px;
}
.chartLabel{
font-size: 1.2em;
text-align: center;
padding: 4px;
}
#boxesLower {
margin: 0 9px;
}
#boxesLower > div {
display: flex;
}
#boxesLower > div > div {
flex: 1 1 auto;
margin: 0 9px 18px 9px;
padding: 10px;
display: flex;
flex-direction: column;
}
.boxLowerHeader{
font-size: 1.3em;
margin: 0 0 5px 10px;
}
#boxStatsLeft{
color: black;
background-color: #cccccc;
}
#boxStatsRight{
color: black;
background-color: #cccccc;
}
.boxStats{
color: white;
}
.boxStatsList{
display: flex;
flex-flow: row wrap;
justify-content: space-around;
opacity: 0.77;
margin-bottom: 5px;
flex: 1 1 auto;
align-content: center;
}
.boxStatsList i.fa{
height: 15px;
width: 33px;
text-align: center;
}
.boxStatsList > div{
padding: 5px 20px;
}
.boxStatsList > div > div{
padding: 3px;
}
div.tooltip {
position: absolute;
text-align: center;
width: 60px;
height: 28px;
padding: 2px;
font: 12px sans-serif;
background: lightsteelblue;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
#tooltip.hidden {
opacity: 0;
}
</style>
<div id="topCharts">
<div class="chartWrapper">
<div class="chartLabel">Pool Historical Hashrate</div>
<div class="chartHolder"><svg id="poolHashrate"/></div>
</div>
</div>
{{ function capitalizeFirstLetter(t){return t.charAt(0).toUpperCase()+t.slice(1)} }}
{{ function readableDate(a){ return new Date(parseInt(a)).toISOString().substring(0, 16).replace('T', ' ') + ' UTC'; } }}
<div class="pure-g-r" id="boxesLower">
{{ for(var pool in it.stats.pools) { }}
<div class="pure-u-1-2">
<div class="boxStats" id="boxStatsLeft">
<div class="boxLowerHeader">{{=it.stats.pools[pool].name}} Pool Stats</div>
<div class="boxStatsList">
<div>
<div><i class="fa fa-users"></i><span id="statsMiners{{=pool}}">{{=it.stats.pools[pool].minerCount}}</span> Miners</div>
<div><i class="fa fa-rocket"></i><span id="statsWorkers{{=pool}}">{{=it.stats.pools[pool].workerCount}}</span> Workers</div>
<div><i class="fa fa-tachometer"></i><span id="statsHashrate{{=pool}}">{{=it.stats.pools[pool].hashrateString}}</span> (Now)</div>
<div><i class="fa fa-tachometer"></i><span id="statsHashrateAvg{{=pool}}">...</span> (Avg)</div>
<div><i class="fa fa-gavel"></i>Luck <span id="statsLuckDays{{=pool}}">{{=it.stats.pools[pool].luckDays}}</span> Days</div>
</div>
</div>
</div>
</div>
<div class="pure-u-1-2">
<div class="boxStats" id="boxStatsRight">
<div class="boxLowerHeader">{{=it.stats.pools[pool].name}} Network Stats</div>
<div class="boxStatsList">
<div>
<div><i class="fa fa-bars" aria-hidden="true"></i><small>Block Height:</small> <span id="statsNetworkBlocks{{=pool}}">{{=it.stats.pools[pool].poolStats.networkBlocks}}</span></div>
<div><i class="fa fa-tachometer"></i><small>Network Hash/s:</small> <span id="statsNetworkSols{{=pool}}">{{=it.stats.pools[pool].poolStats.networkSolsString}}</span></div>
<div><i class="fa fa-unlock-alt" aria-hidden="true"></i><small>Difficulty:</small> <span id="statsNetworkDiff{{=pool}}">{{=it.stats.pools[pool].poolStats.networkDiff}}</span></div>
<div><i class="fa fa-users"></i><small>Node Connections:</small> <span id="statsNetworkConnections{{=pool}}">{{=it.stats.pools[pool].poolStats.networkConnections}}</span></div>
</div>
</div>
</div>
</div>
{{ } }}
</div>
{{ for(var pool in it.stats.pools) { }}
{{ var blockscomb = new Array; }}
<div class="pure-g-r" id="boxesLower">
<div class="pure-u-1-1">
<div class="boxStats" id="boxStatsRight">
<div class="boxLowerHeader">{{=it.stats.pools[pool].name}} Blocks Found &nbsp;&nbsp;
<span style="float:right;"><small>
<i class="fa fa-bars"></i> <span id="statsValidBlocks{{=pool}}">{{=it.stats.pools[pool].poolStats.validBlocks}}</span> Blocks &nbsp;&nbsp;
<i class="fa fa-money"></i> Paid: <span id="statsTotalPaid{{=pool}}">{{=(parseFloat(it.stats.pools[pool].poolStats.totalPaid)).toFixed(8)}}</span> {{=it.stats.pools[pool].symbol}}</small>&nbsp;&nbsp;</span>
</div>
<div class="boxStatsList" style="margin-top: 9px;">
<!--<div id="{{=it.stats.pools[pool].name}}NewBlocks"></div>-->
{{ for(var b in it.stats.pools[pool].pending.blocks) { }}
{{ var block = it.stats.pools[pool].pending.blocks[b].split(":"); }}
<div style="margin-bottom: 9px; background-color: #eeeeee; min-width:600px;" title="{{if (it.stats.pools[pool].pending.confirms && it.stats.pools[pool].pending.confirms[block[0]]) { }}{{if (it.stats.pools[pool].pending.confirms[block[0]] == 1) { }}Waiting for dPoW notarization{{} else if (it.stats.pools[pool].pending.confirms[block[0]] < it.poolsConfigs[pool].paymentProcessing.minConf*2) { }}Waiting for min confirmations{{ } else { }}Queued for payment{{ } }}{{ } else { }}Waiting for payment processor to review{{ } }}">
<i class="fa fa-bars"></i>
<small>Block:</small>
{{if (String(it.stats.pools[pool].name) == "kmd") { }}
<a href="https://kmdexplorer.io/blocks/{{=block[0]}}" target="_blank">{{=block[2]}}</a>
{{ } else if (String(it.stats.pools[pool].name) == "rkt") { }}
<a href="http://rkt.explorer.dexstats.info/block/{{=block[0]}}" target="_blank">{{=block[2]}}</a>
{{ } else if (String(it.stats.pools[pool].name) == "kmdice") { }}
<a href="https://kmdice.explorer.dexstats.info/block/{{=block[0]}}" target="_blank">{{=block[2]}}</a>
{{ } else if (String(it.stats.pools[pool].name) == "hush3z") { }}
<a href="https://hush.explorer.dexstats.info/block/{{=block[0]}}" target="_blank">{{=block[2]}}</a>
{{ } else { }}
{{=block[2]}}
{{ } }}
{{if (block[4] != null) { }}
<span style="padding-left: 18px;"><small>{{=readableDate(block[4])}}</small></span>
{{ } }}
{{if (it.stats.pools[pool].pending.confirms && it.stats.pools[pool].pending.confirms[block[0]]) { }}
{{if (it.stats.pools[pool].pending.confirms[block[0]] == 1) { }}
<span style="float:right; color: red;"><small>Waiting for Notarization</small></span>
{{ } else { }}
<span style="float:right; color: red;"><small>{{=it.stats.pools[pool].pending.confirms[block[0]]}} of {{=it.poolsConfigs[pool].paymentProcessing.minConf*2}} Confirmations</small></span>
{{ } }}
{{ } else { }}
<span style="float:right; color: red;"><small>*PENDING*</small></span>
{{ } }}
<div><i class="fa fa-gavel"></i><small>Mined By:</small> <a href="/workers/{{=block[3].split('.')[0]}}">{{=block[3].length > 40 ? block[3].substring(0, 20) + '...' + block[3].substring(block[3].length-20, block[3].length): block[3]}}</a></div>
</div>
{{ blockscomb.push(block);}}
{{ } }}
{{ var i=0; for(var b in it.stats.pools[pool].confirmed.blocks) { }}
{{ if (i < 8) { i++; }}
{{ var block = it.stats.pools[pool].confirmed.blocks[b].split(":"); }}
<div style="margin-bottom: 9px; background-color: #eeeeee; min-width:600px;"><i class="fa fa-bars"></i>
<small>Block:</small>
{{if (String(it.stats.pools[pool].name) == "kmd") { }}
<a href="https://kmdexplorer.io/blocks/{{=block[0]}}" target="_blank">{{=block[2]}}</a>
{{ } else if (String(it.stats.pools[pool].name).startsWith("rkt")) { }}
<a href="http://rkt.explorer.dexstats.info/block/{{=block[0]}}" target="_blank">{{=block[2]}}</a>
{{ } else if (String(it.stats.pools[pool].name).startsWith("kmdice")) { }}
<a href="http://kmdice.explorer.dexstats.info/block/{{=block[0]}}" target="_blank">{{=block[2]}}</a>
{{ } else if (String(it.stats.pools[pool].name) == "hush3z") { }}
<a href="https://hush.explorer.dexstats.info/block/{{=block[0]}}" target="_blank">{{=block[2]}}</a>
{{ } else { }}
{{=block[2]}}
{{ } }}
{{if (block[4] != null) { }}
<span style="padding-left: 18px;"><small>{{=readableDate(block[4])}}</small></span>
{{ } }}
<span style="float:right; padding-left: 18px; color: green;"><small>*PAID*</small></span>
<div><i class="fa fa-gavel"></i><small>Mined By:</small> <a href="/workers/{{=block[3].split('.')[0]}}">{{=block[3].length > 40 ? block[3].substring(0, 20) + '...' + block[3].substring(block[3].length-20, block[3].length): block[3]}}</a></div>
</div>
{{blockscomb.push(block);}}
{{ } }}
{{ } }}
{{if (blockscomb.length > 0) { }}
<!--{{=JSON.stringify(blockscomb)}}-->
<script>
var blockscomb = ({{=JSON.stringify(blockscomb)}})
</script>
{{ } }}
</div>
</div>
</div>
</div>
{{if (blockscomb.length > 0) { }}
<center><div id="bottomCharts{{=pool}}" style="text-align:center;" align="center">
<div class="chartWrapper" style="text-align:center;">
<div class="chartLabel">Finders of the last {{=blockscomb.length}} blocks</div>
<div class="hidden" id="tooltip{{=pool}}"><p><span id="value{{=pool}}"></span> blocks found by <span id="finderr{{=pool}}"></span></p></div>
<div class="chartHolder" id="pie{{=pool}}"><svg id="blocksPie{{=pool}}" style="display: block; margin: auto; text-align:center;"/></div>
</div>
</div></center>
<script>
var groupedByFinder = {};
var data = [];
for (var i=0; i < blockscomb.length; i++) {
finder=blockscomb[i][3]; // if other doesn 't already have a property for the current letter
// create it and assign it to a new empty array
if (!(finder in groupedByFinder))
groupedByFinder[finder] = [];
groupedByFinder[finder].push(blockscomb[i]);
}
Object.keys(groupedByFinder).forEach(function(i) {
var obj = {};
obj.label = i
obj.value = groupedByFinder[i].length
data.push(obj)
});
console.log(JSON.stringify(data))
var w = 400;
var h = 400;
var r = h/2;
var legendRectSize = 18;
var legendSpacing = 5;
var color = d3.scale.category20c();
var div = d3.select("#pie{{=pool}}").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
var vis = d3.select('#blocksPie{{=pool}}')
.data([data])
.attr("width", 1000)
.attr("height", h)
.attr("style", "display: block; margin: auto;")
.attr("preserveAspectRatio", "xMidYMin")
.append("svg:g")
.attr("transform", "translate(" + r + "," + r + ")");
var pie = d3.layout.pie().value(function(d){return d.value;});
// declare an arc generator function
var arc = d3.svg.arc().outerRadius(r);
// select paths, use arc generator to draw
var arcs = vis.selectAll("g.slice{{=pool}}")
.data(pie)
.enter()
.append("svg:g")
.attr("class", "slice{{=pool}}")
.attr("id", "slice")
.on("mouseover", function(d){
d3.select("#tooltip{{=pool}}")
.style("left", d3.event.pageX + "px")
.style("top", d3.event.pageY + "px")
.style("opacity", 1)
.select("#value{{=pool}}")
.text(d.data.value);
d3.select("#tooltip{{=pool}}")
.select("#finderr{{=pool}}")
.text(d.data.label);
});
arcs.append("svg:path")
.attr("fill", function(d, i){
return color(i);
})
.attr("d", function (d) {
return arc(d);
});
var legend = vis.selectAll('.legend')
.data(color.domain())
.enter()
.append('g')
.attr('class', 'legend')
.attr('id', {{=JSON.stringify(pool)}})
.attr('transform', function(d, i) {
var height = legendRectSize + legendSpacing;
var offset = height * color.domain().length / 2;
var horz = 12 * legendRectSize;
var vert = i * height;
return 'translate(' + horz + ',' + vert + ')';
});
legend.append('rect')
.attr('width', legendRectSize)
.attr('height', legendRectSize)
.style('fill', color)
.style('stroke', color);
legend.append('text')
.attr('x', legendRectSize + legendSpacing)
.attr('y', legendRectSize - legendSpacing)
.text(function(d, i) {
return data[i].label;
});
</script>
{{ } }}
{{ } }}
<script>
document.querySelector('main').appendChild(document.createElement('script')).src = '/static/stats.js';
$(function() {
window.statsSource = new EventSource("/api/live_stats");
statsSource.addEventListener('message', function (e) {
var stats = JSON.parse(e.data);
for (var pool in stats.pools) {
$('#statsMiners' + pool).text(stats.pools[pool].minerCount);
$('#statsWorkers' + pool).text(stats.pools[pool].workerCount);
$('#statsHashrate' + pool).text(stats.pools[pool].hashrateString);
$('#statsHashrateAvg' + pool).text(getReadableHashRateString(calculateAverageHashrate(pool)));
$('#statsLuckDays' + pool).text(stats.pools[pool].luckDays);
$('#statsValidBlocks' + pool).text(stats.pools[pool].poolStats.validBlocks);
$('#statsTotalPaid' + pool).text((parseFloat(stats.pools[pool].poolStats.totalPaid)).toFixed(8));
$('#statsNetworkBlocks' + pool).text(stats.pools[pool].poolStats.networkBlocks);
$('#statsNetworkDiff' + pool).text(stats.pools[pool].poolStats.networkDiff);
$('#statsNetworkSols' + pool).text(getReadableNetworkHashRateString(stats.pools[pool].poolStats.networkSols));
$('#statsNetworkConnections' + pool).text(stats.pools[pool].poolStats.networkConnections);
}
});
});
function getReadableNetworkHashRateString(hashrate){
hashrate = (hashrate * 1000000);
if (hashrate < 1000000)
return '0 Sol';
var byteUnits = [ ' Sol/s', ' KSol/s', ' MSol/s', ' GSol/s', ' TSol/s', ' PSol/s' ];
var i = Math.floor((Math.log(hashrate/1000) / Math.log(1000)) - 1);
hashrate = (hashrate/1000) / Math.pow(1000, i + 1);
return hashrate.toFixed(2) + byteUnits[i];
}
</script>

64
website/minerpond/pages/tbs.html

@ -0,0 +1,64 @@
<style>
#topCharts {
padding: 18px;
}
#topCharts > div > div > svg {
display: block;
height: 280px;
}
.chartWrapper {
border: solid 1px #c7c7c7;
border-radius: 5px;
padding: 5px;
margin-bottom: 18px;
}
.chartLabel {
font-size: 1.2em;
text-align: center;
padding: 4px;
}
.chartHolder {
}
table {
width: 100%;
}
</style>
<table class="pure-table">
<thead>
<tr>
<th>Pool</th>
<th>Algo</th>
<th>Workers</th>
<th>Valid Shares</th>
<th>Invalid Shares</th>
<th>Total Blocks</th>
<th>Pending</th>
<th>Confirmed</th>
<th>Orphaned</th>
<th>Hashrate</th>
</tr>
</thead>
{{ for(var pool in it.stats.pools) { }}
<tr class="pure-table-odd">
<td>{{=it.stats.pools[pool].name}}</td>
<td>{{=it.stats.pools[pool].algorithm}}</td>
<td>{{=Object.keys(it.stats.pools[pool].workers).length}}</td>
<td>{{=it.stats.pools[pool].poolStats.validShares}}</td>
<td>{{=it.stats.pools[pool].poolStats.invalidShares}}</td>
<td>{{=it.stats.pools[pool].poolStats.validBlocks}}</td>
<td>{{=it.stats.pools[pool].blocks.pending}}</td>
<td>{{=it.stats.pools[pool].blocks.confirmed}}</td>
<td>{{=it.stats.pools[pool].blocks.orphaned}}</td>
<td>{{=it.stats.pools[pool].hashrateString}}</td>
</tr>
{{ } }}
</table>

94
website/minerpond/pages/workers.html

@ -0,0 +1,94 @@
<style>
#bottomNotes {
display: block;
padding-left: 18px;
padding-right: 18px;
padding-bottom: 18px;
}
#topPool {
padding-top: 18px;
padding-left: 18px;
padding-right: 18px;
}
#topPool > div > div > svg {
display: block;
height: 280px;
}
.poolWrapper {
border: solid 1px #c7c7c7;
border-radius: 5px;
padding: 5px;
margin-bottom: 18px;
}
.poolLabel {
font-size: 1.2em;
text-align: center;
padding: 4px;
}
.poolMinerTable {
}
table {
width: 100%;
}
</style>
<script type="text/javascript">
function searchKeyPress(e)
{
// look for window.event in case event isn't passed in
e = e || window.event;
if (e.keyCode == 13)
{
document.getElementById('btnSearch').click();
return false;
}
return true;
}
$(document).ready(function(){
$('.btn-lg').click(function(){
window.location = "workers/" + $('.input-lg').val();
});
});
</script>
{{ function capitalizeFirstLetter(t){return t.charAt(0).toUpperCase()+t.slice(1)} }}
{{ var i=0; for(var pool in it.stats.pools) { }}
<div id="topPool">
<div class="poolWrapper">
<div class="poolLabel">
<span style="float:right; margin-bottom: 8px;">
<small>Miner Lookup:
<input type="text" class="form-control input-lg" onkeypress="return searchKeyPress(event);">
<span class="input-group-btn">
<button class="btn btn-default btn-lg" type="button">Lookup</button>
</span>
</small>
</span>
{{=capitalizeFirstLetter(it.stats.pools[pool].name)}} Top Miners &nbsp;&nbsp;
<small><i class="fa fa-users"></i> <span id="statsMiners{{=pool}}">{{=it.stats.pools[pool].minerCount}}</span> Miners &nbsp;&nbsp;
<i class="fa fa-rocket"></i> <span id="statsWorkers{{=pool}}">{{=it.stats.pools[pool].workerCount}}</span> Workers &nbsp;&nbsp;
<i class="fa fa-cog"></i> <span id="statsWorkers{{=pool}}">{{=it.stats.pools[pool].shareCount}}</span> Shares </small>
</div>
<div class="poolMinerTable">
<table class="pure-table">
<thead>
<tr>
<th>Address</th>
<th>Shares</th>
<th>Efficiency</th>
<th>Hashrate</th>
</tr>
</thead>
{{ for(var worker in it.stats.pools[pool].miners) { }}
{{var workerstat = it.stats.pools[pool].miners[worker];}}
<tr class="pure-table-odd">
<td><a href="/workers/{{=worker.split('.')[0]}}">{{=worker.length > 40 ? worker.substring(0, 20) + '...' + worker.substring(worker.length-20, worker.length): worker}}</a></td>
<td>{{=Math.round(workerstat.currRoundShares * 100) / 100}}</td>
<td>{{? workerstat.shares > 0}} {{=Math.floor(10000 * workerstat.shares / (workerstat.shares + workerstat.invalidshares)) / 100}}% {{??}} 0% {{?}}</td>
<td>{{=workerstat.hashrateString}}</td>
</tr>
{{ } }}
</table>
</div>
</div>
</div>
{{ } }}

100
website/minerpond/static/admin.js

@ -0,0 +1,100 @@
var docCookies = {
getItem: function (sKey) {
return decodeURIComponent(document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*" + encodeURIComponent(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=\\s*([^;]*).*$)|^.*$"), "$1")) || null;
},
setItem: function (sKey, sValue, vEnd, sPath, sDomain, bSecure) {
if (!sKey || /^(?:expires|max\-age|path|domain|secure)$/i.test(sKey)) { return false; }
var sExpires = "";
if (vEnd) {
switch (vEnd.constructor) {
case Number:
sExpires = vEnd === Infinity ? "; expires=Fri, 31 Dec 9999 23:59:59 GMT" : "; max-age=" + vEnd;
break;
case String:
sExpires = "; expires=" + vEnd;
break;
case Date:
sExpires = "; expires=" + vEnd.toUTCString();
break;
}
}
document.cookie = encodeURIComponent(sKey) + "=" + encodeURIComponent(sValue) + sExpires + (sDomain ? "; domain=" + sDomain : "") + (sPath ? "; path=" + sPath : "") + (bSecure ? "; secure" : "");
return true;
},
removeItem: function (sKey, sPath, sDomain) {
if (!sKey || !this.hasItem(sKey)) { return false; }
document.cookie = encodeURIComponent(sKey) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT" + ( sDomain ? "; domain=" + sDomain : "") + ( sPath ? "; path=" + sPath : "");
return true;
},
hasItem: function (sKey) {
return (new RegExp("(?:^|;\\s*)" + encodeURIComponent(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=")).test(document.cookie);
}
};
var password = docCookies.getItem('password');
function showLogin(){
$('#adminCenter').hide();
$('#passwordForm').show();
}
function showAdminCenter(){
$('#passwordForm').hide();
$('#adminCenter').show();
}
function tryLogin(){
apiRequest('pools', {}, function(response){
showAdminCenter();
displayMenu(response.result)
});
}
function displayMenu(pools){
$('#poolList').after(Object.keys(pools).map(function(poolName){
return '<li class="poolMenuItem"><a href="#">' + poolName + '</a></li>';
}).join(''));
}
function apiRequest(func, data, callback){
var httpRequest = new XMLHttpRequest();
httpRequest.onreadystatechange = function(){
if (httpRequest.readyState === 4 && httpRequest.responseText){
if (httpRequest.status === 401){
docCookies.removeItem('password');
$('#password').val('');
showLogin();
alert('Incorrect Password');
}
else{
var response = JSON.parse(httpRequest.responseText);
callback(response);
}
}
};
httpRequest.open('POST', '/api/admin/' + func);
data.password = password;
httpRequest.setRequestHeader('Content-Type', 'application/json');
httpRequest.send(JSON.stringify(data));
}
if (password){
tryLogin();
}
else{
showLogin();
}
$('#passwordForm').submit(function(event){
event.preventDefault();
password = $('#password').val();
if (password){
if ($('#remember').is(':checked'))
docCookies.setItem('password', password, Infinity);
else
docCookies.setItem('password', password);
tryLogin();
}
return false;
});

BIN
website/minerpond/static/favicon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 413 B

13
website/minerpond/static/kmdfavicon.svg

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 72 72" style="enable-background:new 0 0 72 72;" xml:space="preserve">
<style type="text/css">
.st0{fill:#316565;}
.st1{fill:#FFFFFF;}
</style>
<rect x="0" y="0" class="st0" width="72" height="72"/>
<path class="st1" d="M35.9,65.4l20.8-8.5l8.7-20.8l-8.5-21L36.1,6.6l-20.8,8.5v0.2L6.6,36.1l8.5,20.8L35.9,65.4z M23,22.9L23,22.9
l13.1-5.5l13.1,5.5l5.5,13.1l-5.5,13.1l-13.1,5.5L23,49.2l-5.5-13.1L23,22.9z"/>
<polygon class="st1" points="36,46.9 43.7,43.7 46.9,35.9 43.7,28.3 36,25.1 28.4,28.3 25.2,35.9 28.2,43.7 "/>
</svg>

After

Width:  |  Height:  |  Size: 797 B

BIN
website/minerpond/static/komodo-logo-horizontal-01.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

13
website/minerpond/static/logo.svg

@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" width="350px" height="350px" viewBox="0 0 350 350" enable-background="new 0 0 350 350" xml:space="preserve" xmlns:xml="http://www.w3.org/XML/1998/namespace">
<g>
<path fill="#F48438" d="M138,346H93c0,0-9,0-9-9s0-18,0-18h63c0,0,0,9,0,18S138,346,138,346z"/>
<path fill="#F48438" d="M255,346h-45c0,0-9,0-9-9s0-18,0-18h63c0,0,0,9,0,18S255,346,255,346z"/>
<path fill="#F48438" d="M30,139c0-63,105.099-79.872,143.186-80C210.458,58.875,318,76,318,139c0,0,0,84.71,0,153c0,27-27,27-27,27 H57c0,0-27,0-27-27V139z"/>
<path fill="#FFFFFF" d="M246,264.056c0,4.643-3.428,8.944-9.12,8.944H112.805c-5.691,0-10.805-4.301-10.805-8.944v-65.15 c0-4.643,5.114-7.906,10.805-7.906H236.88c5.691,0,9.12,3.264,9.12,7.906V264.056z"/>
<circle fill="#FFFFFF" cx="94.667" cy="141.667" r="20.333"/>
<circle fill="#FFFFFF" cx="252.667" cy="141.667" r="20.333"/>
<path fill="#F48438" d="M174.24,45.664c24.69-30.635,70.778-25.601,70.778-25.601l6.603,13.863c0,0-48.596-0.658-62.896,24.075 S149.551,76.3,174.24,45.664z"/>
<circle fill="#F48438" cx="259.083" cy="24.083" r="21.083"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

30
website/minerpond/static/main.js

@ -0,0 +1,30 @@
$(function(){
var hotSwap = function(page, pushSate){
if (pushSate) history.pushState(null, null, '/' + page);
$('.pure-menu-selected').removeClass('pure-menu-selected');
$('a[href="/' + page + '"]').parent().addClass('pure-menu-selected');
$.get("/get_page", {id: page}, function(data){
$('main').html(data);
}, 'html')
};
$('.hot-swapper').click(function(event){
if (event.which !== 1) return;
var pageId = $(this).attr('href').slice(1);
hotSwap(pageId, true);
event.preventDefault();
return false;
});
window.addEventListener('load', function() {
setTimeout(function() {
window.addEventListener("popstate", function(e) {
hotSwap(location.pathname.slice(1));
});
}, 0);
});
window.statsSource = new EventSource("/api/live_stats");
});

246
website/minerpond/static/miner_stats.js

@ -0,0 +1,246 @@
var workerHashrateData;
var workerHashrateChart;
var workerHistoryMax = 160;
var statData;
var totalHash;
var totalImmature;
var totalBal;
var totalPaid;
var totalShares;
function getReadableHashRateString(hashrate){
hashrate = (hashrate * 2);
if (hashrate < 1000000) {
return (Math.round(hashrate / 1000) / 1000 ).toFixed(2)+' Sol/s';
}
var byteUnits = [ ' Sol/s', ' KSol/s', ' MSol/s', ' GSol/s', ' TSol/s', ' PSol/s' ];
var i = Math.floor((Math.log(hashrate/1000) / Math.log(1000)) - 1);
hashrate = (hashrate/1000) / Math.pow(1000, i + 1);
return hashrate.toFixed(2) + byteUnits[i];
}
function timeOfDayFormat(timestamp){
var dStr = d3.time.format('%I:%M %p')(new Date(timestamp));
if (dStr.indexOf('0') === 0) dStr = dStr.slice(1);
return dStr;
}
function getWorkerNameFromAddress(w) {
var worker = w;
if (w.split(".").length > 1) {
worker = w.split(".")[1];
if (worker == null || worker.length < 1) {
worker = "noname";
}
} else {
worker = "noname";
}
return worker;
}
function buildChartData(){
var workers = {};
for (var w in statData.history) {
var worker = getWorkerNameFromAddress(w);
var a = workers[worker] = (workers[worker] || {
hashrate: []
});
for (var wh in statData.history[w]) {
a.hashrate.push([statData.history[w][wh].time * 1000, statData.history[w][wh].hashrate]);
}
if (a.hashrate.length > workerHistoryMax) {
workerHistoryMax = a.hashrate.length;
}
}
var i=0;
workerHashrateData = [];
for (var worker in workers){
workerHashrateData.push({
key: worker,
disabled: (i > Math.min((_workerCount-1), 3)),
values: workers[worker].hashrate
});
i++;
}
}
function updateChartData(){
var workers = {};
for (var w in statData.history) {
var worker = getWorkerNameFromAddress(w);
// get a reference to lastest workerhistory
for (var wh in statData.history[w]) { }
//var wh = statData.history[w][statData.history[w].length - 1];
var foundWorker = false;
for (var i = 0; i < workerHashrateData.length; i++) {
if (workerHashrateData[i].key === worker) {
foundWorker = true;
if (workerHashrateData[i].values.length >= workerHistoryMax) {
workerHashrateData[i].values.shift();
}
workerHashrateData[i].values.push([statData.history[w][wh].time * 1000, statData.history[w][wh].hashrate]);
break;
}
}
if (!foundWorker) {
var hashrate = [];
hashrate.push([statData.history[w][wh].time * 1000, statData.history[w][wh].hashrate]);
workerHashrateData.push({
key: worker,
values: hashrate
});
rebuildWorkerDisplay();
return true;
}
}
triggerChartUpdates();
return false;
}
function calculateAverageHashrate(worker) {
var count = 0;
var total = 1;
var avg = 0;
for (var i = 0; i < workerHashrateData.length; i++) {
count = 0;
for (var ii = 0; ii < workerHashrateData[i].values.length; ii++) {
if (worker == null || workerHashrateData[i].key === worker) {
count++;
avg += parseFloat(workerHashrateData[i].values[ii][1]);
}
}
if (count > total)
total = count;
}
avg = avg / total;
return avg;
}
function triggerChartUpdates(){
workerHashrateChart.update();
}
function displayCharts() {
nv.addGraph(function() {
workerHashrateChart = nv.models.lineChart()
.margin({left: 80, right: 30})
.x(function(d){ return d[0] })
.y(function(d){ return d[1] })
.useInteractiveGuideline(true);
workerHashrateChart.xAxis.tickFormat(timeOfDayFormat);
workerHashrateChart.yAxis.tickFormat(function(d){
return getReadableHashRateString(d);
});
d3.select('#workerHashrate').datum(workerHashrateData).call(workerHashrateChart);
return workerHashrateChart;
});
}
function updateStats() {
totalHash = statData.totalHash;
totalPaid = statData.paid;
totalBal = statData.balance;
totalImmature = statData.immature;
totalShares = statData.totalShares;
// do some calculations
var _blocktime = 250;
var _networkHashRate = parseFloat(statData.networkSols) * 1.2;
var _myHashRate = (totalHash / 1000000) * 2;
var luckDays = ((_networkHashRate / _myHashRate * _blocktime) / (24 * 60 * 60)).toFixed(3);
// update miner stats
$("#statsHashrate").text(getReadableHashRateString(totalHash));
$("#statsHashrateAvg").text(getReadableHashRateString(calculateAverageHashrate(null)));
$("#statsLuckDays").text(luckDays);
$("#statsTotalImmature").text(totalImmature);
$("#statsTotalBal").text(totalBal);
$("#statsTotalPaid").text(totalPaid);
$("#statsTotalShares").text(totalShares.toFixed(2));
}
function updateWorkerStats() {
// update worker stats
var i=0;
for (var w in statData.workers) { i++;
var htmlSafeWorkerName = w.split('.').join('_').replace(/[^\w\s]/gi, '');
var saneWorkerName = getWorkerNameFromAddress(w);
$("#statsHashrate"+htmlSafeWorkerName).text(getReadableHashRateString(statData.workers[w].hashrate));
$("#statsHashrateAvg"+htmlSafeWorkerName).text(getReadableHashRateString(calculateAverageHashrate(saneWorkerName)));
$("#statsLuckDays"+htmlSafeWorkerName).text(statData.workers[w].luckDays);
$("#statsPaid"+htmlSafeWorkerName).text(statData.workers[w].paid);
$("#statsBalance"+htmlSafeWorkerName).text(statData.workers[w].balance);
$("#statsShares"+htmlSafeWorkerName).text(Math.round(statData.workers[w].currRoundShares * 100) / 100);
$("#statsDiff"+htmlSafeWorkerName).text(statData.workers[w].diff);
}
}
function addWorkerToDisplay(name, htmlSafeName, workerObj) {
var htmlToAdd = "";
htmlToAdd = '<div class="boxStats" id="boxStatsLeft" style="float:left; margin: 9px; min-width: 260px;"><div class="boxStatsList">';
if (htmlSafeName.indexOf("_") >= 0) {
htmlToAdd+= '<div class="boxLowerHeader">'+htmlSafeName.substr(htmlSafeName.indexOf("_")+1,htmlSafeName.length)+'</div>';
} else {
htmlToAdd+= '<div class="boxLowerHeader">noname</div>';
}
htmlToAdd+='<div><i class="fa fa-tachometer"></i> <span id="statsHashrate'+htmlSafeName+'">'+getReadableHashRateString(workerObj.hashrate)+'</span> (Now)</div>';
htmlToAdd+='<div><i class="fa fa-tachometer"></i> <span id="statsHashrateAvg'+htmlSafeName+'">'+getReadableHashRateString(calculateAverageHashrate(name))+'</span> (Avg)</div>';
htmlToAdd+='<div><i class="fa fa-shield"></i> <small>Diff:</small> <span id="statsDiff'+htmlSafeName+'">'+workerObj.diff+'</span></div>';
htmlToAdd+='<div><i class="fa fa-cog"></i> <small>Shares:</small> <span id="statsShares'+htmlSafeName+'">'+(Math.round(workerObj.currRoundShares * 100) / 100)+'</span></div>';
htmlToAdd+='<div><i class="fa fa-gavel"></i> <small>Luck <span id="statsLuckDays'+htmlSafeName+'">'+workerObj.luckDays+'</span> Days</small></div>';
htmlToAdd+='<div><i class="fa fa-money"></i> <small>Bal: <span id="statsBalance'+htmlSafeName+'">'+workerObj.balance+'</span></small></div>';
htmlToAdd+='<div><i class="fa fa-money"></i> <small>Paid: <span id="statsPaid'+htmlSafeName+'">'+workerObj.paid+'</span></small></div>';
htmlToAdd+='</div></div></div>';
$("#boxesWorkers").html($("#boxesWorkers").html()+htmlToAdd);
}
function rebuildWorkerDisplay() {
$("#boxesWorkers").html("");
var i=0;
for (var w in statData.workers) { i++;
var htmlSafeWorkerName = w.split('.').join('_').replace(/[^\w\s]/gi, '');
var saneWorkerName = getWorkerNameFromAddress(w);
addWorkerToDisplay(saneWorkerName, htmlSafeWorkerName, statData.workers[w]);
}
}
// resize chart on window resize
nv.utils.windowResize(triggerChartUpdates);
// grab initial stats
$.getJSON('/api/worker_stats?'+_miner, function(data){
statData = data;
for (var w in statData.workers) { _workerCount++; }
buildChartData();
displayCharts();
rebuildWorkerDisplay();
updateStats();
});
// live stat updates
statsSource.addEventListener('message', function(e){
// TODO, create miner_live_stats...
// miner_live_stats will return the same josn except without the worker history
// FOR NOW, use this to grab updated stats
$.getJSON('/api/worker_stats?'+_miner, function(data){
statData = data;
// check for missing workers
var wc = 0;
var rebuilt = false;
// update worker stats
for (var w in statData.workers) { wc++; }
// TODO, this isn't 100% fool proof!
if (_workerCount != wc) {
if (_workerCount > wc) {
rebuildWorkerDisplay();
rebuilt = true;
}
_workerCount = wc;
}
rebuilt = (rebuilt || updateChartData());
updateStats();
if (!rebuilt) {
updateWorkerStats();
}
});
});

1
website/minerpond/static/nvd3.css

File diff suppressed because one or more lines are too long

6
website/minerpond/static/nvd3.js

File diff suppressed because one or more lines are too long

143
website/minerpond/static/stats.js

@ -0,0 +1,143 @@
var poolHashrateData;
var poolHashrateChart;
var statData;
var poolKeys;
function buildChartData(){
var pools = {};
poolKeys = [];
for (var i = 0; i < statData.length; i++){
for (var pool in statData[i].pools){
if (poolKeys.indexOf(pool) === -1)
poolKeys.push(pool);
}
}
for (var i = 0; i < statData.length; i++) {
var time = statData[i].time * 1000;
for (var f = 0; f < poolKeys.length; f++){
var pName = poolKeys[f];
var a = pools[pName] = (pools[pName] || {
hashrate: []
});
if (pName in statData[i].pools){
a.hashrate.push([time, statData[i].pools[pName].hashrate]);
}
else{
a.hashrate.push([time, 0]);
}
}
}
poolHashrateData = [];
for (var pool in pools){
poolHashrateData.push({
key: pool,
values: pools[pool].hashrate
});
$('#statsHashrateAvg' + pool).text(getReadableHashRateString(calculateAverageHashrate(pool)));
}
}
function calculateAverageHashrate(pool) {
var count = 0;
var total = 1;
var avg = 0;
for (var i = 0; i < poolHashrateData.length; i++) {
count = 0;
for (var ii = 0; ii < poolHashrateData[i].values.length; ii++) {
if (pool == null || poolHashrateData[i].key === pool) {
count++;
avg += parseFloat(poolHashrateData[i].values[ii][1]);
}
}
if (count > total)
total = count;
}
avg = avg / total;
return avg;
}
function getReadableHashRateString(hashrate){
hashrate = (hashrate * 2);
if (hashrate < 1000000) {
return (Math.round(hashrate / 1000) / 1000 ).toFixed(2)+' Sol/s';
}
var byteUnits = [ ' Sol/s', ' KSol/s', ' MSol/s', ' GSol/s', ' TSol/s', ' PSol/s' ];
var i = Math.floor((Math.log(hashrate/1000) / Math.log(1000)) - 1);
hashrate = (hashrate/1000) / Math.pow(1000, i + 1);
return hashrate.toFixed(2) + byteUnits[i];
}
function timeOfDayFormat(timestamp){
var dStr = d3.time.format('%I:%M %p')(new Date(timestamp));
if (dStr.indexOf('0') === 0) dStr = dStr.slice(1);
return dStr;
}
function displayCharts(){
nv.addGraph(function() {
poolHashrateChart = nv.models.lineChart()
.margin({left: 80, right: 30})
.x(function(d){ return d[0] })
.y(function(d){ return d[1] })
.useInteractiveGuideline(true);
poolHashrateChart.xAxis.tickFormat(timeOfDayFormat);
poolHashrateChart.yAxis.tickFormat(function(d){
return getReadableHashRateString(d);
});
d3.select('#poolHashrate').datum(poolHashrateData).call(poolHashrateChart);
return poolHashrateChart;
});
}
function triggerChartUpdates(){
poolHashrateChart.update();
}
nv.utils.windowResize(triggerChartUpdates);
$.getJSON('/api/pool_stats', function(data){
statData = data;
buildChartData();
displayCharts();
});
statsSource.addEventListener('message', function(e){
var stats = JSON.parse(e.data);
statData.push(stats);
var newPoolAdded = (function(){
for (var p in stats.pools){
if (poolKeys.indexOf(p) === -1)
return true;
}
return false;
})();
if (newPoolAdded || Object.keys(stats.pools).length > poolKeys.length){
buildChartData();
displayCharts();
}
else {
var time = stats.time * 1000;
for (var f = 0; f < poolKeys.length; f++) {
var pool = poolKeys[f];
for (var i = 0; i < poolHashrateData.length; i++) {
if (poolHashrateData[i].key === pool) {
poolHashrateData[i].values.shift();
poolHashrateData[i].values.push([time, pool in stats.pools ? stats.pools[pool].hashrate : 0]);
$('#statsHashrateAvg' + pool).text(getReadableHashRateString(calculateAverageHashrate(pool)));
break;
}
}
}
triggerChartUpdates();
}
});

68
website/minerpond/static/style.css

@ -0,0 +1,68 @@
html, button, input, select, textarea, .pure-g [class *= "pure-u"], .pure-g-r [class *= "pure-u"]{
font-family: 'Open Sans', sans-serif;
}
html{
background: #2d2d2d;
overflow-y: scroll;
}
body{
display: flex;
flex-direction: column;
max-width: 1160px;
margin: 0 auto;
}
header > .home-menu{
background: inherit !important;
height: 54px;
display: flex;
}
header > .home-menu > a.pure-menu-heading, header > .home-menu > ul, header > .home-menu > ul > li{
display: flex !important;
align-items: center;
justify-content: center;
line-height: normal !important;
}
header > .home-menu > a.pure-menu-heading{
color: white;
font-size: 1.5em;
}
header > .home-menu > ul > li > a{
color: #ced4d9;
}
header > .home-menu > ul > li > a:hover, header > .home-menu > ul > li > a:focus{
background: inherit !important;
}
header > .home-menu > ul > li > a:hover, header > .home-menu > ul > li.pure-menu-selected > a{
color: white;
}
main{
background-color: #ebf4fa;
position: relative;
}
footer{
text-align: center;
color: #b3b3b3;
text-decoration: none;
font-size: 0.8em;
padding: 15px;
line-height: 24px;
}
footer a{
color: #fff;
text-decoration: none;
}
footer iframe{
vertical-align: middle;
}
Loading…
Cancel
Save