Add https://github.com/mesh0000/poolui to the repository
This commit is contained in:
parent
aa510ce285
commit
365febf1e4
62 changed files with 3451 additions and 0 deletions
21
frontend/LICENSE
Normal file
21
frontend/LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2017 Snipa22 <Alexander Blair, alex@snipanet.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
87
frontend/app/admin.html
Normal file
87
frontend/app/admin.html
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
<!DOCTYPE html>
|
||||
<!--[if lt IE 7]> <html lang="en" ng-app="pooladmin" class="no-js lt-ie9 lt-ie8 lt-ie7" manifest="app.manifest"> <![endif]-->
|
||||
<!--[if IE 7]> <html lang="en" ng-app="pooladmin" class="no-js lt-ie9 lt-ie8" manifest="app.manifest"> <![endif]-->
|
||||
<!--[if IE 8]> <html lang="en" ng-app="pooladmin" class="no-js lt-ie9" manifest="app.manifest"> <![endif]-->
|
||||
<!--[if gt IE 8]><!--> <html lang="en" ng-app="pooladmin" class="no-js" manifest="app.manifest"> <!--<![endif]-->
|
||||
<head>
|
||||
<title>XMRPool.net - Mine XMR/Monero or BTC/Bitcoin</title>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
|
||||
<!-- CSS -->
|
||||
<link rel="stylesheet" href="vendor/angular-material/angular-material.css">
|
||||
<link rel="stylesheet" href="vendor/n3-charts/build/LineChart.css">
|
||||
<link rel="stylesheet" href="vendor/angular-material-data-table/dist/md-data-table.css">
|
||||
<link rel="stylesheet" href="app.css">
|
||||
<!-- Fonts -->
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,600,700" rel="stylesheet">
|
||||
<!-- CSS -->
|
||||
<link rel="apple-touch-icon" sizes="76x76" href="assets/assets/apple-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="96x96" href="assets/assets/favicon.png">
|
||||
</head>
|
||||
<body ng-cloak layout="column" ng-controller="AppCtrl">
|
||||
<!--[if lt IE 7]>
|
||||
<p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
|
||||
<![endif]-->
|
||||
|
||||
<div class="container" layout="row" flex>
|
||||
<md-card flex>
|
||||
<md-toolbar class="md-table-toolbar md-warn">
|
||||
<div class="md-toolbar-tools" flex>
|
||||
<h1 flex>Pool Admin CP</h1>
|
||||
<div flex></div>
|
||||
<div ng-if="isLoggedIn()">
|
||||
<md-button class="md-accent" ng-click="logout()">Logout</md-button>
|
||||
</div>
|
||||
</div>
|
||||
</md-toolbar>
|
||||
<md-card-content class="no-padding" ng-if="isLoggedIn()">
|
||||
<md-nav-bar md-selected-nav-item="currentNavItem" nav-bar-aria-label="navigation links">
|
||||
<md-nav-item md-nav-href="#dashboard" name="dashboard">
|
||||
Dashboard
|
||||
</md-nav-item>
|
||||
<md-nav-item md-nav-href="#workers" name="workers">
|
||||
Workers
|
||||
</md-nav-item>
|
||||
<md-nav-item md-nav-href="#ports" name="ports">
|
||||
Ports
|
||||
</md-nav-item>
|
||||
<md-nav-item md-nav-href="#config" name="config">
|
||||
Config
|
||||
</md-nav-item>
|
||||
</md-nav-bar>
|
||||
</md-card-content>
|
||||
<md-card-content>
|
||||
<ng-view flex></ng-view>
|
||||
</md-card-content>
|
||||
</md-card>
|
||||
</div>
|
||||
<!-- Scripts -->
|
||||
<script src="vendor/jquery/dist/jquery.js"></script>
|
||||
<script src="vendor/lodash/dist/lodash.js"></script>
|
||||
<script src="vendor/moment/moment.js"></script>
|
||||
<script src="vendor/angular/angular.js"></script>
|
||||
<script src="vendor/angular-route/angular-route.js"></script>
|
||||
<script src="vendor/angular-animate/angular-animate.js"></script>
|
||||
<script src="vendor/angular-aria/angular-aria.js"></script>
|
||||
<script src="vendor/angular-material/angular-material.js"></script>
|
||||
<script src="vendor/angular-moment/angular-moment.js"></script>
|
||||
<script src="vendor/d3/d3.js"></script>
|
||||
<script src="vendor/n3-charts/build/LineChart.js"></script>
|
||||
<script src="vendor/angular-material-data-table/dist/md-data-table.js"></script>
|
||||
<script src="vendor/ngstorage/ngStorage.js"></script>
|
||||
<script src="globals.js"></script>
|
||||
<script src="utils/dataservice.js"></script>
|
||||
<script src="utils/strings.js"></script>
|
||||
<script src="admin.js"></script>
|
||||
<script src="admin/adminlogin.js"></script>
|
||||
<script src="admin/dashboard.js"></script>
|
||||
<script src="admin/workers.js"></script>
|
||||
<script src="admin/ports.js"></script>
|
||||
<script src="admin/config.js"></script>
|
||||
<script src="admin/editconfig.js"></script>
|
||||
<script src="admin/editport.js"></script>
|
||||
<script src="admin/addport.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
64
frontend/app/admin.js
Normal file
64
frontend/app/admin.js
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
'use strict';
|
||||
|
||||
// Declare app level module which depends on views, and components
|
||||
var app = angular.module('pooladmin', [
|
||||
'pool.globals',
|
||||
'ngRoute',
|
||||
'ngMaterial',
|
||||
'md.data.table',
|
||||
'ngStorage',
|
||||
'angularMoment',
|
||||
'utils.xhr',
|
||||
'utils.strings'
|
||||
]).config(['$locationProvider', '$routeProvider', '$mdThemingProvider', function($locationProvider, $routeProvider, $mdThemingProvider) {
|
||||
$locationProvider.hashPrefix('');
|
||||
// $mdIconProvider.defaultIconSet("https://rawgit.com/angular/material-start/es5-tutorial/app/assets/svg/avatars.svg", 128)
|
||||
|
||||
$mdThemingProvider.theme('default')
|
||||
.primaryPalette('grey')
|
||||
.accentPalette('light-blue');
|
||||
|
||||
$routeProvider
|
||||
.when('/login', {
|
||||
templateUrl: 'admin/adminlogin.html',
|
||||
controller: 'AdminLoginCtrl'
|
||||
})
|
||||
.when('/dashboard', {
|
||||
templateUrl: 'admin/dashboard.html',
|
||||
controller: 'AdminDashCtrl'
|
||||
})
|
||||
.when('/workers', {
|
||||
templateUrl: 'admin/workers.html',
|
||||
controller: 'AdminWorkersCtrl'
|
||||
})
|
||||
.when('/ports', {
|
||||
templateUrl: 'admin/ports.html',
|
||||
controller: 'AdminPortsCtrl'
|
||||
})
|
||||
.when('/config', {
|
||||
templateUrl: 'admin/config.html',
|
||||
controller: 'AdminConfigCtrl'
|
||||
})
|
||||
|
||||
$routeProvider.otherwise({redirectTo: '/login'});
|
||||
|
||||
}]);
|
||||
|
||||
app.controller('AppCtrl', function($scope, $window, $route, $location, $interval, dataService, $localStorage, GLOBALS) {
|
||||
$scope.GLOBALS = GLOBALS;
|
||||
|
||||
var loginCheck = function (){
|
||||
if(!dataService.isLoggedIn()){
|
||||
$location.path('#login');
|
||||
}
|
||||
}
|
||||
|
||||
$scope.isLoggedIn = function () {
|
||||
return dataService.isLoggedIn();
|
||||
}
|
||||
|
||||
$scope.logout = function () {
|
||||
dataService.logout();
|
||||
$location.path('#login');
|
||||
}
|
||||
});
|
||||
26
frontend/app/admin/addport.js
Normal file
26
frontend/app/admin/addport.js
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
app.controller('addPortCtrl', function ($scope, $mdDialog, dataService) {
|
||||
'use strict';
|
||||
|
||||
this.cancel = function (){
|
||||
//config.value=old_value;
|
||||
$mdDialog.cancel();
|
||||
}
|
||||
|
||||
this.edit = function () {
|
||||
$scope.item.form.$setSubmitted();
|
||||
|
||||
if($scope.item.form.$valid) {
|
||||
dataService.putData('/admin/ports', {id: config.id, value: config.value}, function(data) {
|
||||
$mdDialog.hide(data);
|
||||
}, function (e) {
|
||||
// error
|
||||
$scope.config = old_value;
|
||||
$mdDialog.hide('error');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
});
|
||||
39
frontend/app/admin/adminlogin.html
Normal file
39
frontend/app/admin/adminlogin.html
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
<md-content class="login" layout-align="center center">
|
||||
<md-toolbar>
|
||||
<div class="md-toolbar-tools">
|
||||
<h2 flex>Pool Admin Login</h2>
|
||||
<span flex></span>
|
||||
</div>
|
||||
</md-toolbar>
|
||||
|
||||
<md-dialog-content layout-padding >
|
||||
<span flex="30"></span>
|
||||
<form name="loginForm">
|
||||
<div></div>
|
||||
<md-input-container class="md-icon-float md-block">
|
||||
<!-- Use floating label instead of placeholder -->
|
||||
<label>Payment Address</label>
|
||||
<md-icon>person</md-icon>
|
||||
<input ng-model="admin.username" type="text" md-autofocus>
|
||||
</md-input-container>
|
||||
|
||||
<md-input-container class="md-icon-float md-block">
|
||||
<label>Password</label>
|
||||
<md-icon>lock</md-icon>
|
||||
<input ng-model="admin.password" type="password" placeholder="default: your email inentifier (see FAQ)">
|
||||
</md-input-container>
|
||||
</form>
|
||||
<span flex="30"></span>
|
||||
</md-dialog-content>
|
||||
|
||||
<md-dialog-actions layout="row">
|
||||
<md-checkbox class="md-primary" ng-model="remember">
|
||||
Remember me
|
||||
</md-checkbox>
|
||||
<span flex></span>
|
||||
<p class="invalid">{{status}}</p>
|
||||
<md-button class="md-raised md-primary" ng-click="login()">
|
||||
Login
|
||||
</md-button>
|
||||
</md-dialog-actions>
|
||||
</md-content>
|
||||
30
frontend/app/admin/adminlogin.js
Normal file
30
frontend/app/admin/adminlogin.js
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
'use strict';
|
||||
|
||||
app.controller('AdminLoginCtrl', function($scope, $location, $route, dataService) {
|
||||
$scope.admin = {
|
||||
username:"",
|
||||
password:""
|
||||
}
|
||||
|
||||
$scope.login = function () {
|
||||
dataService.postData("/authenticate", $scope.admin, function(data){
|
||||
if (data.success){
|
||||
data.remember = $scope.remember;
|
||||
dataService.setAuthToken(data);
|
||||
$location.path('#/dashboard');
|
||||
} else {
|
||||
// $mdDialog.hide(false);
|
||||
}
|
||||
}, function(error){
|
||||
$scope.status = "Please check your login details";
|
||||
});
|
||||
}
|
||||
|
||||
var isLoggedIn = function () {
|
||||
if(dataService.isLoggedIn == false) ;
|
||||
}
|
||||
|
||||
if(dataService.isLoggedIn()) {
|
||||
$location.path('/dashboard');
|
||||
};
|
||||
});
|
||||
48
frontend/app/admin/config.html
Normal file
48
frontend/app/admin/config.html
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
<md-content flex>
|
||||
<md-toolbar class="md-table-toolbar md-default" ng-show="!selected.length">
|
||||
<div class="md-toolbar-tools">
|
||||
<span>Pool Config</span>
|
||||
<span flex></span>
|
||||
<!-- <md-button class="md-icon-button" ng-click="loadStuff()">
|
||||
<md-icon>add</md-icon>
|
||||
</md-button> -->
|
||||
|
||||
</div>
|
||||
</md-toolbar>
|
||||
|
||||
<md-toolbar class="md-table-toolbar alternate" ng-show="selected.length">
|
||||
<div class="md-toolbar-tools">
|
||||
<span>{{selected.length}} {{selected.length > 1 ? 'items' : 'item'}} selected</span>
|
||||
<span flex></span>
|
||||
|
||||
</div>
|
||||
</md-toolbar>
|
||||
<md-table-container>
|
||||
<table md-table data-ng-model="selected" md-progress="promise" md-row-select>
|
||||
<thead md-head>
|
||||
<tr md-row>
|
||||
<th></th>
|
||||
<th md-column><h2 class="md-subhead">ID</h2></th>
|
||||
<th md-column><h2 class="md-subhead">Item</h2></th>
|
||||
<th md-column><h2 class="md-subhead">Value</h2></th>
|
||||
<th md-column><h2 class="md-subhead">Type</h2></th>
|
||||
<th md-column><h2 class="md-subhead">Module</h2></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody md-body>
|
||||
<tr md-row ng-repeat="config in pool_configs">
|
||||
<td md-cell>
|
||||
<md-button class="md-icon-button" ng-click="editConfig($event, config)">
|
||||
<md-icon>mode_edit</md-icon>
|
||||
</md-button>
|
||||
</td>
|
||||
<td md-cell>{{config.id}}</td>
|
||||
<td md-cell>{{config.item}}</td>
|
||||
<td md-cell>{{config.value}}</td>
|
||||
<td md-cell>{{config.type}}</td>
|
||||
<td md-cell>{{config.module}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</md-table-container>
|
||||
</md-content>
|
||||
32
frontend/app/admin/config.js
Normal file
32
frontend/app/admin/config.js
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
'use strict';
|
||||
|
||||
app.controller('AdminConfigCtrl', function($scope, $location, $route, $mdDialog, dataService) {
|
||||
$scope.selected = [];
|
||||
|
||||
var loadConfig = function () {
|
||||
dataService.getData("/admin/config", function(data) {
|
||||
$scope.pool_configs = data;
|
||||
});
|
||||
}
|
||||
|
||||
$scope.editConfig = function (ev, config) {
|
||||
$mdDialog.show({
|
||||
locals: {
|
||||
config: config
|
||||
},
|
||||
clickOutsideToClose: true,
|
||||
controller: 'editConfigCtrl',
|
||||
controllerAs: 'ctrl',
|
||||
focusOnOpen: false,
|
||||
targetEvent: ev,
|
||||
templateUrl: 'admin/editconfig.html',
|
||||
}).then (function () {
|
||||
loadConfig();
|
||||
}, function(){
|
||||
// error
|
||||
})
|
||||
|
||||
};
|
||||
|
||||
loadConfig();
|
||||
});
|
||||
131
frontend/app/admin/dashboard.html
Normal file
131
frontend/app/admin/dashboard.html
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
<md-content>
|
||||
<div layout="row">
|
||||
<md-card ng-repeat="(type, stats) in pool_stats" flex>
|
||||
<md-toolbar class="md-table-toolbar md-default">
|
||||
<div class="md-toolbar-tools">
|
||||
<h1>{{type}}</h1>
|
||||
</div>
|
||||
</md-toolbar>
|
||||
<md-card-content>
|
||||
<md-list flex>
|
||||
<md-list-item>
|
||||
<h3> Owed </h3>
|
||||
<p class="text-right">{{stats.owed | toXMR | number }} XMR</p>
|
||||
</md-list-item>
|
||||
<md-list-item>
|
||||
<h3> Paid </h3>
|
||||
<p class="text-right">{{stats.paid | toXMR | number }} XMR</p>
|
||||
</md-list-item>
|
||||
<md-list-item>
|
||||
<h3> Mined </h3>
|
||||
<p class="text-right">{{stats.mined | toXMR | number}} XMR</p>
|
||||
</md-list-item>
|
||||
<md-list-item>
|
||||
<h3> Shares </h3>
|
||||
<p class="text-right">{{stats.shares | number }}</p>
|
||||
</md-list-item>
|
||||
<md-list-item>
|
||||
<h3> Target Shares </h3>
|
||||
<p class="text-right">{{stats.targetShares | number}}</p>
|
||||
</md-list-item>
|
||||
</md-card-content>
|
||||
</md-card>
|
||||
</div>
|
||||
|
||||
<md-card>
|
||||
<md-toolbar class="md-table-toolbar md-default">
|
||||
<div class="md-toolbar-tools">
|
||||
<h1>Wallet</h1>
|
||||
</div>
|
||||
</md-toolbar>
|
||||
<md-card-content>
|
||||
<div flex layout="column" layout-md="row" layout-gt-md="row" class="text-center">
|
||||
<div flex layout="column" layout-gt-xs="row">
|
||||
<div flex>
|
||||
<h4> Height<h2>{{ pool_wallet.height | number }}</h2></h4>
|
||||
</div>
|
||||
<div flex>
|
||||
<h4> Unlocked<h2>{{ pool_wallet.unlocked | toXMR }} XMR</h2></h4>
|
||||
</div>
|
||||
</div>
|
||||
<div flex layout="column" layout-gt-xs="row">
|
||||
<div flex>
|
||||
<h4> Balance<h2>{{ pool_wallet.balance | toXMR }} XMR</h2></h4>
|
||||
</div>
|
||||
<div flex>
|
||||
<h4> Timestamp<h2>{{ pool_wallet.ts | date }}</h2></h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</md-card-content>
|
||||
</md-card>
|
||||
|
||||
<md-card>
|
||||
<md-card-content flex>
|
||||
<md-content>
|
||||
<md-table-container>
|
||||
<table md-table md-row-select ng-model="selected" md-progress="promise">
|
||||
<thead md-head>
|
||||
<tr md-row>
|
||||
<th md-column><h2 class="md-subhead">Height</h2></th>
|
||||
<th md-column><h2 class="md-subhead">Unlocked</h2></th>
|
||||
<th md-column><h2 class="md-subhead">Balanced</h2></th>
|
||||
<th md-column><h2 class="md-subhead">Timestamp</h2></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody md-body>
|
||||
<tr md-row ng-repeat="history in pool_wallet_history">
|
||||
<td md-cell><p class="md-body-2">{{history.height | number}}</p></td>
|
||||
<td md-cell><p class="md-body-2">{{history.balance | toXMR }} XMR</p></td>
|
||||
<td md-cell><p class="md-body-2">{{history.unlocked | toXMR }} XMR</p></td>
|
||||
<td md-cell>
|
||||
<h3 class="md-body-2" am-time-ago="history.ts"></h3>
|
||||
<md-tooltip>
|
||||
({{history.ts | date:'hh:mm:ss dd/MM/yy'}})
|
||||
</md-tooltip>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</md-table-container>
|
||||
<!-- <md-table-pagination md-limit="options.limit" md-limit-options="[10, 20, 30]" md-page="options.page" md-total="{{poolStats.global.totalPayments}}" md-on-paginate="loadPayments" md-page-select></md-table-pagination> -->
|
||||
</md-content>
|
||||
</md-card-content>
|
||||
</md-card>
|
||||
</md-content>
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- <md-table-pagination md-limit="options.limit" md-limit-options="[15, 30, 50]" md-page="options.page" md-total="{{miner.txnCount}}" md-on-paginate="loadPayments" md-page-select></md-table-pagination>
|
||||
<md-table-container>
|
||||
<table md-table md-row-select ng-model="selected" md-progress="promise">
|
||||
<thead md-head>
|
||||
<tr md-row>
|
||||
<th md-column><h2 class="md-subhead">Time Sent</h2></th>
|
||||
<th md-column><h2 class="md-subhead">Transaction Hash</h2></th>
|
||||
<th md-column><h2 class="md-subhead">Amount</h2></th>
|
||||
<th md-column><h2 class="md-subhead">Fee</h2></th>
|
||||
<th md-column><h2 class="md-subhead">Mixin</h2></th>
|
||||
<th md-column><h2 class="md-subhead">Payees</h2></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody md-body>
|
||||
<tr md-row ng-repeat="payment in payments.global">
|
||||
<td md-cell>
|
||||
<h3 class="md-body-2" am-time-ago="payment.ts"></h3>
|
||||
<md-tooltip>
|
||||
{{payment.ts | date:'hh:mm:ss dd/MM/yy'}}
|
||||
</md-tooltip>
|
||||
</td>
|
||||
<td md-cell ng-bind-html="payment.hash | hashToLink: 'tx'"></td>
|
||||
<td md-cell><p class="md-body-2">{{payment.value | toXMR}} XMR</p></td>
|
||||
<td md-cell><p class="md-body-2">{{payment.fee | toXMR }} XMR</p></td>
|
||||
<td md-cell><p class="md-body-2">{{payment.mixins}}</p></td>
|
||||
<td md-cell><p class="md-body-2">{{payment.payees}}</p></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</md-table-container>
|
||||
<md-table-pagination md-limit="options.limit" md-limit-options="[15, 30, 50]" md-page="options.page" md-total="{{miner.txnCount}}" md-on-paginate="loadPayments" md-page-select></md-table-pagination>
|
||||
</md-card-content> -->
|
||||
17
frontend/app/admin/dashboard.js
Normal file
17
frontend/app/admin/dashboard.js
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
'use strict';
|
||||
|
||||
app.controller('AdminDashCtrl', function($scope, $location, $route, dataService) {
|
||||
$scope.selected = [];
|
||||
|
||||
dataService.getData("/admin/stats", function(data) {
|
||||
$scope.pool_stats = data;
|
||||
});
|
||||
|
||||
dataService.getData("/admin/wallet", function(data) {
|
||||
$scope.pool_wallet = data;
|
||||
});
|
||||
|
||||
$scope.promise = dataService.getData("/admin/wallet/history", function(data) {
|
||||
$scope.pool_wallet_history = data;
|
||||
});
|
||||
});
|
||||
27
frontend/app/admin/editconfig.html
Normal file
27
frontend/app/admin/editconfig.html
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<md-dialog add-item-dialog >
|
||||
<md-dialog-content class="md-dialog-content">
|
||||
|
||||
<h2 class="md-title">Edit Config</h2>
|
||||
|
||||
<p ng-if="item.form.$submitted && item.form.$invalid" class="md-warn" layout="row" layout-align="start center">
|
||||
<i class="material-icons">error_outline</i>
|
||||
<span> All fields are required.</span>
|
||||
</p>
|
||||
|
||||
<form name="item.form" ng-submit="ctrl.edit()">
|
||||
<div layout="row" class="input-row">
|
||||
<md-input-container flex>
|
||||
<label>{{config.item}}</label>
|
||||
<input name="value" ng-model="config.value" required>
|
||||
</md-input-container>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</md-dialog-content>
|
||||
|
||||
<md-dialog-actions>
|
||||
<md-button class="md-accent" ng-click="ctrl.edit()" ng-disabled="item.form.$invalid">Save</md-button>
|
||||
<md-button class="md-warn" ng-click="ctrl.cancel()">Cancel</md-button>
|
||||
</md-dialog-actions>
|
||||
|
||||
</md-dialog>
|
||||
29
frontend/app/admin/editconfig.js
Normal file
29
frontend/app/admin/editconfig.js
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
app.controller('editConfigCtrl', function ($scope, $mdDialog, dataService, config) {
|
||||
'use strict';
|
||||
|
||||
$scope.config = config;
|
||||
var old_value = config.value;
|
||||
|
||||
this.cancel = function (){
|
||||
config.value=old_value;
|
||||
$mdDialog.cancel();
|
||||
}
|
||||
|
||||
this.edit = function () {
|
||||
$scope.item.form.$setSubmitted();
|
||||
|
||||
if($scope.item.form.$valid) {
|
||||
dataService.putData('/admin/config', {id: config.id, value: config.value}, function(data) {
|
||||
$mdDialog.hide(data);
|
||||
}, function (e) {
|
||||
// error
|
||||
$scope.config = old_value;
|
||||
$mdDialog.hide('error');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
});
|
||||
52
frontend/app/admin/editport.html
Normal file
52
frontend/app/admin/editport.html
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
<md-dialog add-item-dialog >
|
||||
<md-dialog-content class="md-dialog-content">
|
||||
|
||||
<h2 class="md-title">Edit Port</h2>
|
||||
|
||||
<p ng-if="item.form.$submitted && item.form.$invalid" class="md-warn" layout="row" layout-align="start center">
|
||||
<i class="material-icons">error_outline</i>
|
||||
<span> All fields are required.</span>
|
||||
</p>
|
||||
<form name="item.form" ng-submit="ctrl.edit()">
|
||||
<div layout="column" class="input-column">
|
||||
<md-input-container flex>
|
||||
<label>Port</label>
|
||||
<input name="portNum" ng-model="port.port" required>
|
||||
</md-input-container>
|
||||
<md-input-container flex>
|
||||
<label>Description</label>
|
||||
<input name="desc" ng-model="port.desc" required>
|
||||
</md-input-container>
|
||||
<md-input-container flex>
|
||||
<label>Difficulty</label>
|
||||
<input name="diff" ng-model="port.diff" required>
|
||||
</md-input-container>
|
||||
<md-input-container flex>
|
||||
<label>Type</label>
|
||||
<md-select ng-model="port.portType">
|
||||
<md-option value="pplns"><em>PPLNS</em></md-option>
|
||||
<md-option value="pps"><em>PPS</em></md-option>
|
||||
<md-option value="solo"><em>SOLO</em></md-option>
|
||||
</md-select>
|
||||
</md-input-container>
|
||||
<md-input-container flex>
|
||||
<md-checkbox name="value" ng-model="port.hidden" ng-true-value="1" ng-false-value="0">
|
||||
Hidden
|
||||
</md-checkbox>
|
||||
</md-input-container>
|
||||
<md-input-container flex>
|
||||
<md-checkbox name="value" ng-model="port.ssl" ng-true-value="1" ng-false-value="0">
|
||||
SSL
|
||||
</md-checkbox>
|
||||
</md-input-container>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</md-dialog-content>
|
||||
|
||||
<md-dialog-actions>
|
||||
<md-button class="md-accent" ng-click="ctrl.edit()" ng-disabled="item.form.$invalid">Save</md-button>
|
||||
<md-button class="md-warn" ng-click="ctrl.cancel()">Cancel</md-button>
|
||||
</md-dialog-actions>
|
||||
|
||||
</md-dialog>
|
||||
32
frontend/app/admin/editport.js
Normal file
32
frontend/app/admin/editport.js
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
app.controller('editPortCtrl', function ($scope, $mdDialog, dataService, port) {
|
||||
'use strict';
|
||||
var old_value = angular.copy(port);
|
||||
|
||||
port.hidden = (port.hidden) ? 1 : 0;
|
||||
port.ssl = (port.ssl) ? 1 : 0;
|
||||
|
||||
$scope.port = port;
|
||||
|
||||
this.cancel = function (){
|
||||
angular.copy(old_value, port);
|
||||
$mdDialog.cancel();
|
||||
}
|
||||
|
||||
this.edit = function () {
|
||||
$scope.item.form.$setSubmitted();
|
||||
|
||||
if($scope.item.form.$valid) {
|
||||
dataService.putData('/admin/ports', $scope.port, function(data) {
|
||||
$mdDialog.hide(data);
|
||||
}, function (e) {
|
||||
// error
|
||||
$scope.port = old_value;
|
||||
$mdDialog.hide('error');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
});
|
||||
70
frontend/app/admin/ports.html
Normal file
70
frontend/app/admin/ports.html
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
<md-content flex>
|
||||
<md-toolbar class="md-table-toolbar md-default" ng-show="!selected.length">
|
||||
<div class="md-toolbar-tools">
|
||||
<span>Ports</span>
|
||||
<span flex></span>
|
||||
<md-button class="md-accent" ng-click="addPort(ev)">
|
||||
<md-icon>add</md-icon>
|
||||
Add Port
|
||||
</md-button>
|
||||
|
||||
</div>
|
||||
</md-toolbar>
|
||||
|
||||
<md-toolbar class="md-table-toolbar alternate" ng-show="selected.length">
|
||||
<div class="md-toolbar-tools">
|
||||
<span>{{selected.length}} {{selected.length > 1 ? 'items' : 'item'}} selected</span>
|
||||
<span flex></span>
|
||||
</div>
|
||||
</md-toolbar>
|
||||
<md-table-container>
|
||||
<table md-table data-ng-model="selected" md-progress="promise" md-row-select>
|
||||
<thead md-head>
|
||||
<tr md-row>
|
||||
<th></th>
|
||||
<th md-column><h2 class="md-subhead">Port</h2></th>
|
||||
<th md-column><h2 class="md-subhead">Difficulty</h2></th>
|
||||
<th md-column><h2 class="md-subhead">Description</h2></th>
|
||||
<th md-column><h2 class="md-subhead">Type</h2></th>
|
||||
<th md-column><h2 class="md-subhead">Hidden</h2></th>
|
||||
<th md-column><h2 class="md-subhead">SSL</h2></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody md-body>
|
||||
<tr md-row ng-repeat="port in pool_ports">
|
||||
<td md-cell>
|
||||
<md-button class="md-icon-button" ng-click="editPort($event, port)">
|
||||
<md-icon>mode_edit</md-icon>
|
||||
</md-button>
|
||||
</td>
|
||||
<td md-cell>{{port.port}}</td>
|
||||
<td md-cell>{{port.diff}}</td>
|
||||
<td md-cell>{{port.desc}}</td>
|
||||
<td md-cell>{{port.portType}}</td>
|
||||
<td md-cell>
|
||||
<md-icon ng-if="port.hidden" class="valid">
|
||||
done
|
||||
</md-icon>
|
||||
<md-icon ng-if="!port.hidden" class="invalid">
|
||||
clear
|
||||
</md-icon>
|
||||
</td>
|
||||
<td md-cell>
|
||||
<md-icon ng-if="port.ssl" class="valid">
|
||||
done
|
||||
</md-icon>
|
||||
<md-icon ng-if="!port.ssl" class="invalid">
|
||||
clear
|
||||
</md-icon>
|
||||
</td>
|
||||
<td md-cell>
|
||||
<md-button class="md-icon-button" ng-click="deletePort($event, port)">
|
||||
<md-icon>delete</md-icon>
|
||||
</md-button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</md-table-container>
|
||||
</md-content>
|
||||
67
frontend/app/admin/ports.js
Normal file
67
frontend/app/admin/ports.js
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
'use strict';
|
||||
|
||||
app.controller('AdminPortsCtrl', function($scope, $location, $route, $mdDialog, dataService) {
|
||||
$scope.selected = [];
|
||||
|
||||
var loadPorts = function() {
|
||||
dataService.getData("/admin/ports", function(data) {
|
||||
$scope.pool_ports = data;
|
||||
});
|
||||
}
|
||||
|
||||
$scope.editPort = function (ev, port) {
|
||||
$mdDialog.show({
|
||||
locals: {
|
||||
port: port
|
||||
},
|
||||
clickOutsideToClose: true,
|
||||
controller: 'editPortCtrl',
|
||||
controllerAs: 'ctrl',
|
||||
focusOnOpen: true,
|
||||
targetEvent: ev,
|
||||
templateUrl: 'admin/editport.html',
|
||||
}).then (function () {
|
||||
loadPorts();
|
||||
}, function(msg){
|
||||
port=msg;
|
||||
})
|
||||
};
|
||||
|
||||
$scope.addPort = function(ev){
|
||||
$mdDialog.show({
|
||||
clickOutsideToClose: true,
|
||||
controller: 'addPortCtrl',
|
||||
controllerAs: 'ctrl',
|
||||
focusOnOpen: true,
|
||||
targetEvent: ev,
|
||||
templateUrl: 'admin/editport.html',
|
||||
}).then (function () {
|
||||
loadPorts();
|
||||
}, function(){
|
||||
// error
|
||||
})
|
||||
}
|
||||
|
||||
$scope.deletePort = function (ev, port) {
|
||||
console.log(port);
|
||||
var confirm = $mdDialog.confirm()
|
||||
.title('Delete Port ' + port.port +' ?')
|
||||
.textContent('Are you sure you want to get delete port ' + port.port + '?')
|
||||
.ariaLabel('Delete Port')
|
||||
.targetEvent(ev)
|
||||
.ok("Delete")
|
||||
.cancel("Cancel");
|
||||
|
||||
$mdDialog.show(confirm).then(function() {
|
||||
// ;p
|
||||
dataService.deleteData("/admin/ports", {port: port.port}, function(data){
|
||||
// successfully deleted
|
||||
loadPorts();
|
||||
});
|
||||
}, function() {
|
||||
// cancel do nothing
|
||||
});
|
||||
};
|
||||
|
||||
loadPorts();
|
||||
});
|
||||
39
frontend/app/admin/workers.html
Normal file
39
frontend/app/admin/workers.html
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
<md-content flex>
|
||||
<!-- <md-table-pagination md-limit="options.limit" md-limit-options="[10, 20, 30]" md-page="options.page" md-total="{{poolStats.global.totalPayments}}" md-on-paginate="loadPayments" md-page-select></md-table-pagination> -->
|
||||
<md-table-container>
|
||||
<table md-table md-row-select ng-model="selected" md-progress="promise">
|
||||
<thead md-head md-order="workersOrder">
|
||||
<tr md-row>
|
||||
<th md-column md-numeric md-order-by="hashRate"><h2 class="md-subhead">Hashrate</h2></th>
|
||||
<th md-column><h2 class="md-subhead">Address</h2></th>
|
||||
<th md-column md-numeric md-order-by="paid"><h2 class="md-subhead">Paid</h2></th>
|
||||
<th md-column md-numeric md-order-by="due"><h2 class="md-subhead">Due</h2></th>
|
||||
<th md-column md-numeric md-order-by="totalHashes"><h2 class="md-subhead">Total #s</h2></th>
|
||||
<th md-column md-numeric md-order-by="goodShares"><h2 class="md-subhead">Good #s</h2></th>
|
||||
<th md-column md-numeric md-order-by="badShares"><h2 class="md-subhead">Bad #s</h2></th>
|
||||
<th md-column md-numeric md-order-by="workers.length"><h2 class="md-subhead">Workers</h2></th>
|
||||
<th md-column><h2 class="md-subhead">Last Hash</h2></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody md-body>
|
||||
<tr md-row ng-repeat="worker in pool_workers | orderBy: workersOrder">
|
||||
<td md-cell>{{worker.hashRate | toHashRate}}</td>
|
||||
<td md-cell>{{worker.address}}</td>
|
||||
<td md-cell>{{worker.paid | toXMR}} XMR</td>
|
||||
<td md-cell>{{worker.due | toXMR }} XMR</td>
|
||||
<td md-cell>{{worker.totalHashes | number }}</td>
|
||||
<td md-cell>{{worker.goodShares | number }}</td>
|
||||
<td md-cell>{{worker.badShares | number }}</td>
|
||||
<td md-cell>{{worker.workers.length}}</td>
|
||||
<td md-cell>
|
||||
<h3 class="md-body-2" am-time-ago="worker.lastHash"></h3>
|
||||
<md-tooltip>
|
||||
{{worker.lastHash | date:'hh:mm:ss dd/MM/yy'}}
|
||||
</md-tooltip>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</md-table-container>
|
||||
<!-- <md-table-pagination md-limit="options.limit" md-limit-options="[10, 20, 30]" md-page="options.page" md-total="{{poolStats.global.totalPayments}}" md-on-paginate="loadPayments" md-page-select></md-table-pagination> -->
|
||||
</md-content>
|
||||
9
frontend/app/admin/workers.js
Normal file
9
frontend/app/admin/workers.js
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
'use strict';
|
||||
|
||||
app.controller('AdminWorkersCtrl', function($scope, $location, $route, dataService) {
|
||||
$scope.selected = [];
|
||||
|
||||
$scope.promise = dataService.getData("/admin/userList", function(data) {
|
||||
$scope.pool_workers = data;
|
||||
});
|
||||
});
|
||||
225
frontend/app/app.css
Normal file
225
frontend/app/app.css
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
/*** Global Styles ***/
|
||||
|
||||
html, body {
|
||||
font-family: 'Source Sans Pro', sans-serif;
|
||||
font-size:14px;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
min-width: 420px;
|
||||
}
|
||||
|
||||
a {
|
||||
transition: color .4s;
|
||||
color: #3287b3;
|
||||
}
|
||||
|
||||
a:link { text-decoration: none; },
|
||||
a:visited { color: #3b98c8; }
|
||||
a:hover { color: #3b98c8; }
|
||||
a:active {
|
||||
transition: color .3s;
|
||||
color: #007BE6;
|
||||
}
|
||||
|
||||
/*** Material Styles ***/
|
||||
#main {
|
||||
background: #f3f5f7;
|
||||
}
|
||||
|
||||
.maintoolbar {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
|
||||
.statsbar md-card-title-text{
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.statsbar md-card md-card-title {
|
||||
padding: 12px ;
|
||||
}
|
||||
|
||||
.md-menu-toolbar {
|
||||
background: #00796B;
|
||||
}
|
||||
|
||||
md-tooltip .md-content {
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
.navbar .smallfont h3{
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
.navbar .smallfont h3 b{
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.sidenav {
|
||||
background-color: #2b333e;
|
||||
}
|
||||
|
||||
.sidenav ul{
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.sidenav md-list-item {
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.sidenav a {
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
color: #fff;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.sidebar a {
|
||||
color: rgb(66,66,66);
|
||||
text-decoration: none;
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
line-height: 24px;
|
||||
letter-spacing: 0;
|
||||
opacity: 0.87;
|
||||
}
|
||||
|
||||
.sidebar md-icon, .sidebar ng-md-icon {
|
||||
vertical-align: middle;
|
||||
padding-left: 40px;
|
||||
}
|
||||
|
||||
#content {
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
.logo {
|
||||
background: #333d4b !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.logo i {
|
||||
color: #00b0ff;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* HELPERS */
|
||||
.text-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.text-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.truncate{
|
||||
width:30%;
|
||||
white-space:nowrap;
|
||||
overflow:hidden;
|
||||
text-overflow:ellipsis
|
||||
}
|
||||
|
||||
.hide-error-msg .md-errors-spacer{
|
||||
display: none
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
position: relative;
|
||||
width: 72px;
|
||||
height: 72px;
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
margin: 0px;
|
||||
vertical-align: middle;
|
||||
zoom:0.6;
|
||||
transform: translateZ(0);
|
||||
-webkit-transform: scale(0.60);
|
||||
-moz-transform:scale(0.60);
|
||||
font-size: 72px !important;
|
||||
color: #aeb7c2;
|
||||
}
|
||||
|
||||
.valid {
|
||||
color: green !important;
|
||||
}
|
||||
|
||||
.invalid {
|
||||
color: red !important;
|
||||
}
|
||||
|
||||
.metric md-icon {
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.navbar .login {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
/* CHARTS */
|
||||
|
||||
.chart-legend {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.chartcontainer {
|
||||
width: 100% !important;
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.chartcontainer .ng-isolate-scope {
|
||||
height:300px;
|
||||
}
|
||||
|
||||
/* LOGIN WINDOW */
|
||||
md-dialog .login{
|
||||
min-width: 800px !important;
|
||||
}
|
||||
|
||||
.console {
|
||||
height: 300px;
|
||||
min-width: 500px;
|
||||
}
|
||||
|
||||
.no-padding {
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.power {
|
||||
width: 100%;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.power a {
|
||||
color: #f2ff2f;
|
||||
}
|
||||
|
||||
.power a:hover {
|
||||
color: #f2f;
|
||||
}
|
||||
|
||||
.power a.alt {
|
||||
color: #f2f;
|
||||
}
|
||||
|
||||
.power a.alt:hover {
|
||||
color: #f2ff2f;
|
||||
}
|
||||
|
||||
.fixed {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
.theironfist {
|
||||
cursor: pointer;
|
||||
}
|
||||
230
frontend/app/app.js
Normal file
230
frontend/app/app.js
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
'use strict';
|
||||
|
||||
// Declare app level module which depends on views, and components
|
||||
var app = angular.module('poolui', [
|
||||
'pool.globals',
|
||||
'ngRoute',
|
||||
'ngMaterial',
|
||||
'md.data.table',
|
||||
'angularMoment',
|
||||
'ngStorage',
|
||||
'ngAudio',
|
||||
'utils.strings',
|
||||
'utils.services',
|
||||
'utils.xhr',
|
||||
'n3-line-chart',
|
||||
'angular-page-visibility'
|
||||
]).config(['$locationProvider', '$routeProvider', '$mdThemingProvider', function($locationProvider, $routeProvider, $mdThemingProvider) {
|
||||
$locationProvider.hashPrefix('')
|
||||
;
|
||||
$mdThemingProvider.theme('default')
|
||||
.primaryPalette('grey')
|
||||
.accentPalette('light-blue');
|
||||
|
||||
$routeProvider
|
||||
.when('/home', {
|
||||
templateUrl: 'user/home/home.html',
|
||||
controller: 'HomeCtrl',
|
||||
activetab: 'home'
|
||||
})
|
||||
.when('/dashboard', {
|
||||
templateUrl: 'user/dashboard/dashboard.html',
|
||||
controller: 'DashboardCtrl',
|
||||
activetab: 'dashboard'
|
||||
})
|
||||
.when('/blocks', {
|
||||
templateUrl: 'user/blocks/blocks.html',
|
||||
controller: 'BlocksCtrl',
|
||||
activetab: 'blocks'
|
||||
})
|
||||
.when('/payments', {
|
||||
templateUrl: 'user/payments/payments.html',
|
||||
controller: 'PaymentsCtrl',
|
||||
activetab: 'payments'
|
||||
})
|
||||
.when('/network', {
|
||||
templateUrl: 'user/network/network.html',
|
||||
controller: 'NetworkCtrl',
|
||||
activetab: 'network'
|
||||
})
|
||||
.when('/ports', {
|
||||
templateUrl: 'user/ports/ports.html',
|
||||
controller: 'PortsCtrl',
|
||||
activetab: 'ports'
|
||||
})
|
||||
.when('/help/chat', {
|
||||
templateUrl: 'user/help/chat.html',
|
||||
controller: 'ChatCtrl',
|
||||
activetab: 'support'
|
||||
})
|
||||
.when('/help/getting_started', {
|
||||
templateUrl: 'user/help/getting_started.html',
|
||||
controller: 'GettingStartedCtrl',
|
||||
activetab: 'help'
|
||||
})
|
||||
.when('/help/faq', {
|
||||
templateUrl: 'user/help/faq.html',
|
||||
controller: 'FAQCtrl',
|
||||
activetab: 'help'
|
||||
});
|
||||
|
||||
$routeProvider.otherwise({redirectTo: '/home'});
|
||||
|
||||
}]);
|
||||
|
||||
app.controller('AppCtrl', function($scope, $rootScope, $location, $route, $routeParams, $anchorScroll, $window, $interval, $mdDialog, dataService, timerService, addressService, $mdSidenav, $mdMedia, $localStorage, ngAudio, GLOBALS){
|
||||
$scope.GLOBALS = GLOBALS;
|
||||
var appCache = window.applicationCache;
|
||||
$scope.$storage = $localStorage;
|
||||
|
||||
$scope.poolList = ["pplns", "pps", "solo"];
|
||||
$scope.poolStats = {}; // All Pool stats
|
||||
$scope.addrStats = {}; // All tracked addresses
|
||||
$scope.lastBlock = {};
|
||||
|
||||
// for miner tracking
|
||||
$scope.yourTotalHashRate = 0;
|
||||
|
||||
// Hashrate Alarm
|
||||
$scope.globalSiren = false;
|
||||
$scope.sirenAudio = ngAudio.load("assets/ding.wav");
|
||||
|
||||
// Update global hashrate and set off alarm if any of the tracked addresses fall below the threshold
|
||||
var updateHashRate = function (addrStats){
|
||||
var totalHashRate = 0;
|
||||
var siren = false;
|
||||
|
||||
_.each(addrStats, function(addr,index) {
|
||||
totalHashRate += addr.hash;
|
||||
if (addr.alarm && addr.hash < addr.alarmLimit) {
|
||||
siren=true;
|
||||
}
|
||||
});
|
||||
|
||||
$scope.globalSiren=siren;
|
||||
$scope.yourTotalHashRate = totalHashRate;
|
||||
}
|
||||
|
||||
var playSiren = function (){
|
||||
($scope.globalSiren) ? $scope.sirenAudio.play() : $scope.sirenAudio.stop();
|
||||
}
|
||||
|
||||
// ------- UI HELPERS
|
||||
|
||||
$scope.menuOpen = $mdMedia('gt-md');
|
||||
$scope.$watch(function() { return $mdMedia('gt-md'); }, function(big) {
|
||||
$scope.menuOpen = $mdMedia('gt-md');
|
||||
});
|
||||
|
||||
$scope.toggleSidenav = function (){
|
||||
if (!$mdMedia('gt-md')) {
|
||||
$mdSidenav('left').toggle();
|
||||
} else {
|
||||
// toggle boolean
|
||||
$scope.menuOpen = !$scope.menuOpen;
|
||||
}
|
||||
}
|
||||
|
||||
// ------- Miner Login and auth
|
||||
$scope.minerLogin = function (ev) {
|
||||
$mdDialog.show({
|
||||
controller: "LoginCtrl",
|
||||
templateUrl: 'user/home/login.html',
|
||||
parent: angular.element(document.body),
|
||||
targetEvent: ev,
|
||||
clickOutsideToClose:true,
|
||||
fullscreen: !$scope.menuOpen // Only for -xs, -sm breakpoints.
|
||||
})
|
||||
.then(function(answer) {
|
||||
// success callback
|
||||
}, function(error) {
|
||||
// error callback
|
||||
});
|
||||
}
|
||||
|
||||
$scope.minerConsole = function (ev) {
|
||||
$mdDialog.show({
|
||||
locals: $scope.config,
|
||||
controller: "ConsoleCtrl",
|
||||
templateUrl: 'user/home/console.html',
|
||||
parent: angular.element(document.body),
|
||||
targetEvent: ev,
|
||||
clickOutsideToClose:true,
|
||||
fullscreen: !$scope.menuOpen // Only for -xs, -sm breakpoints.
|
||||
})
|
||||
.then(function(answer){
|
||||
if(answer=='logout'){
|
||||
dataService.logout();
|
||||
}
|
||||
}, function(reason){
|
||||
// console.log(reason);
|
||||
});
|
||||
}
|
||||
|
||||
$scope.isLoggedIn = function() {
|
||||
return dataService.isLoggedIn();
|
||||
}
|
||||
|
||||
// ------- App Update
|
||||
var update = function() {
|
||||
if (appCache.status == window.applicationCache.UPDATEREADY) {
|
||||
appCache.swapCache();
|
||||
$window.location.reload();
|
||||
}
|
||||
}
|
||||
|
||||
appCache.addEventListener("updateready", function(event) {
|
||||
update();
|
||||
}, false);
|
||||
|
||||
var updateCache = function () {
|
||||
appCache.update();
|
||||
}
|
||||
|
||||
// API Requests
|
||||
var loadData = function () {
|
||||
dataService.getData("/pool/stats", function(data){
|
||||
$scope.poolList = data.pool_list;
|
||||
$scope.poolStats.global = data.pool_statistics;
|
||||
});
|
||||
|
||||
dataService.getData("/network/stats", function(data){
|
||||
$scope.network = data;
|
||||
});
|
||||
}
|
||||
|
||||
var loadOnce = function () {
|
||||
dataService.getData("/config", function(data){
|
||||
$scope.config = data;
|
||||
});
|
||||
}
|
||||
|
||||
// For FAQ
|
||||
$rootScope.$on('$routeChangeSuccess', function(newRoute, oldRoute) {
|
||||
$location.hash($routeParams.scrollTo);
|
||||
$anchorScroll();
|
||||
});
|
||||
|
||||
// Start doing things
|
||||
loadOnce();
|
||||
loadData();
|
||||
update();
|
||||
|
||||
// Start the timer and register global requests
|
||||
timerService.startTimer(GLOBALS.api_refresh_interval);
|
||||
timerService.register(loadData, 'global');
|
||||
$interval(updateCache, GLOBALS.app_update_interval); // check for app updates every 5 mins
|
||||
|
||||
// Start address tracking servuce after starting timer, only one callback supported at a time
|
||||
addressService.start(function(addrStats) {
|
||||
$scope.addrStats = addrStats;
|
||||
updateHashRate(addrStats);
|
||||
playSiren();
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
// Sponsor
|
||||
$scope.sponsor_open = false
|
||||
|
||||
});
|
||||
BIN
frontend/app/assets/cloudsigma.png
Normal file
BIN
frontend/app/assets/cloudsigma.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
BIN
frontend/app/assets/ding.wav
Normal file
BIN
frontend/app/assets/ding.wav
Normal file
Binary file not shown.
BIN
frontend/app/assets/sponsor-logo.png
Normal file
BIN
frontend/app/assets/sponsor-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
12
frontend/app/globals.default.js
Normal file
12
frontend/app/globals.default.js
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('pool.globals', [])
|
||||
|
||||
.factory('GLOBALS', function() {
|
||||
return {
|
||||
pool_name: "XMRPool.net",
|
||||
api_url : 'https://api.xmrpool.net',
|
||||
api_refresh_interval: 5000,
|
||||
app_update_interval: 30*60000
|
||||
};
|
||||
});
|
||||
12
frontend/app/globals.js
Normal file
12
frontend/app/globals.js
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('pool.globals', [])
|
||||
|
||||
.factory('GLOBALS', function() {
|
||||
return {
|
||||
pool_name: "XMRPool.net",
|
||||
api_url : 'https://api.xmrpool.net',
|
||||
api_refresh_interval: 5000,
|
||||
app_update_interval: 5*60000
|
||||
};
|
||||
});
|
||||
158
frontend/app/index.html
Normal file
158
frontend/app/index.html
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
<!DOCTYPE html>
|
||||
<!--[if lt IE 7]> <html lang="en" ng-app="poolui" class="no-js lt-ie9 lt-ie8 lt-ie7" manifest="app.manifest"> <![endif]-->
|
||||
<!--[if IE 7]> <html lang="en" ng-app="poolui" class="no-js lt-ie9 lt-ie8" manifest="app.manifest"> <![endif]-->
|
||||
<!--[if IE 8]> <html lang="en" ng-app="poolui" class="no-js lt-ie9" manifest="app.manifest"> <![endif]-->
|
||||
<!--[if gt IE 8]><!--> <html lang="en" ng-app="poolui" class="no-js" manifest="app.manifest"> <!--<![endif]-->
|
||||
<head>
|
||||
<title>XMRPool.net - Mine XMR/Monero or BTC/Bitcoin</title>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
|
||||
<!-- CSS -->
|
||||
<link rel="stylesheet" href="vendor/angular-material/angular-material.css">
|
||||
<link rel="stylesheet" href="vendor/n3-charts/build/LineChart.css">
|
||||
<link rel="stylesheet" href="vendor/angular-material-data-table/dist/md-data-table.css">
|
||||
<link rel="stylesheet" href="app.css">
|
||||
<!-- Fonts -->
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,600,700" rel="stylesheet">
|
||||
<!-- CSS -->
|
||||
<link rel="apple-touch-icon" sizes="76x76" href="assets/assets/apple-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="96x96" href="assets/assets/favicon.png">
|
||||
</head>
|
||||
<body ng-cloak layout="column" ng-controller="AppCtrl">
|
||||
<!--[if lt IE 7]>
|
||||
<p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
|
||||
<![endif]-->
|
||||
|
||||
<div class="container" layout="row" flex>
|
||||
<md-sidenav md-is-locked-open="menuOpen" md-component-id="left" md-whiteframe="4" class="sidenav">
|
||||
<md-toolbar class="logo">
|
||||
<h1 class="md-toolbar-tools">XMRPool.net <i class="md-caption">beta</i></h1>
|
||||
</md-toolbar>
|
||||
<md-list>
|
||||
<md-list-item>
|
||||
<md-button href="#/home" ng-class="isActivePage('home')">
|
||||
<md-icon md-font-set="material-icons" class="menu-item">home</md-icon>
|
||||
Home
|
||||
</md-button>
|
||||
</md-list-item>
|
||||
<md-list-item>
|
||||
<md-button href="#/dashboard" ng-class="isActivePage('dashboard')">
|
||||
<md-icon md-font-set="material-icons" class="menu-item">dashboard</md-icon>
|
||||
Dashboard
|
||||
</md-button>
|
||||
</md-list-item>
|
||||
<md-list-item>
|
||||
<md-button href="#/blocks" ng-class="isActivePage('blocks')">
|
||||
<md-icon md-font-set="material-icons" class="menu-item">reorder</md-icon>
|
||||
Blocks
|
||||
</md-button>
|
||||
</md-list-item>
|
||||
<md-list-item>
|
||||
<md-button href="#/payments" ng-class="isActivePage('payments')">
|
||||
<md-icon md-font-set="material-icons" class="menu-item">payments</md-icon>
|
||||
Payment
|
||||
</md-button>
|
||||
</md-list-item>
|
||||
<md-list-item>
|
||||
<md-button href="#/ports" ng-class="isActivePage('ports')">
|
||||
<md-icon md-font-set="material-icons" class="menu-item">flight_land</md-icon>
|
||||
Ports
|
||||
</md-button>
|
||||
</md-list-item>
|
||||
<md-list-item>
|
||||
<md-button href="#/network" ng-class="isActivePage('network')">
|
||||
<md-icon md-font-set="material-icons" class="menu-item">language</md-icon>
|
||||
Network
|
||||
</md-button>
|
||||
</md-list-item>
|
||||
<md-list-item>
|
||||
<md-button href="#/help/chat" ng-class="isActivePage('support')">
|
||||
<md-icon md-font-set="material-icons" class="menu-item">group</md-icon>
|
||||
Support
|
||||
</md-button>
|
||||
</md-list-item>
|
||||
<md-list-item>
|
||||
<md-button href="#/help/getting_started" ng-class="isActivePage('help/getting_started')">
|
||||
<md-icon md-font-set="material-icons" class="menu-item">launch</md-icon>
|
||||
Getting Started
|
||||
</md-button>
|
||||
</md-list-item>
|
||||
<md-list-item>
|
||||
<md-button href="#/help/faq" ng-class="isActivePage('help/faq')">
|
||||
<md-icon md-font-set="material-icons" class="menu-item">help_outline</md-icon>
|
||||
FAQ
|
||||
</md-button>
|
||||
</md-list-item>
|
||||
</md-list>
|
||||
<div flex></div>
|
||||
<div class="power text-center md-subhead" layout="row" layout-align="center end">
|
||||
<div flex>
|
||||
Powered by <a href="https://github.com/Snipa22/nodejs-pool" target="_new">nodejs-pool</a> & <a class="alt" href="https://github.com/mesh0000/poolui" target="_new">poolui</a>
|
||||
<br/>
|
||||
MIT Licence
|
||||
</div>
|
||||
</div>
|
||||
</md-sidenav>
|
||||
|
||||
<md-content flex id="main">
|
||||
<md-toolbar class="navbar md-whiteframe-2dp">
|
||||
<div class="md-toolbar-tools maintoolbar text-center" ng-class="{smallfont: !menuOpen}" layout="row">
|
||||
<md-button class="md-icon-button" type="button" ng-click="toggleSidenav()">
|
||||
<md-icon md-font-set="material-icons">menu</md-icon>
|
||||
</md-button>
|
||||
<h3 flex><a href="#/dashboard">Network : <b>{{network.difficulty | difficultyToHashRate | toHashRate}}</b></a></h3>
|
||||
<h3 flex><a href="#/dashboard">Pool : <b>{{poolStats.global.hashRate | toHashRate}}</b></a></h3>
|
||||
<h3 flex><a href="#/dashboard">You : <b>{{yourTotalHashRate | toHashRate}}</b></a></h3>
|
||||
<div>
|
||||
<md-button class="md-raised md-accent" aria-label="Miner Login" ng-click="minerLogin($event)" ng-if="!isLoggedIn()">
|
||||
<md-icon class="login">fingerprint</md-icon>
|
||||
Login
|
||||
</md-button>
|
||||
<md-button class="md-fab md-mini md-warn" aria-label="Miner Login" ng-click="minerConsole($event)" ng-if="isLoggedIn()">
|
||||
<md-icon class="login">build</md-icon>
|
||||
</md-button>
|
||||
</div>
|
||||
</div>
|
||||
</md-toolbar>
|
||||
<div id="content" ng-view flex></div>
|
||||
</md-content>
|
||||
</div>
|
||||
<!-- Scripts -->
|
||||
<script src="vendor/jquery/dist/jquery.js"></script>
|
||||
<script src="vendor/moment/moment.js"></script>
|
||||
<script src="vendor/lodash/dist/lodash.js"></script>
|
||||
<script src="vendor/angular/angular.js"></script>
|
||||
<script src="vendor/angular-route/angular-route.js"></script>
|
||||
<script src="vendor/angular_page_visibility/dist/page_visibility.js"></script>
|
||||
<script src="vendor/angular-animate/angular-animate.js"></script>
|
||||
<script src="vendor/randomcolor/randomColor.js"></script>
|
||||
<script src="vendor/d3/d3.js"></script>
|
||||
<script src="vendor/n3-charts/build/LineChart.js"></script>
|
||||
<script src="vendor/angular-aria/angular-aria.js"></script>
|
||||
<script src="vendor/angular-material/angular-material.js"></script>
|
||||
<script src="vendor/angular-material-data-table/dist/md-data-table.js"></script>
|
||||
<script src="vendor/ngstorage/ngStorage.js"></script>
|
||||
<script src="vendor/angular-moment/angular-moment.js"></script>
|
||||
<script src="vendor/angular-audio/app/angular.audio.js"></script>
|
||||
<script src="globals.js"></script>
|
||||
<script src="utils/strings.js"></script>
|
||||
<script src="utils/services.js"></script>
|
||||
<script src="utils/dataservice.js"></script>
|
||||
<script src="app.js"></script>
|
||||
<script src="user/home/home.js"></script>
|
||||
<script src="user/home/login.js"></script>
|
||||
<script src="user/home/console.js"></script>
|
||||
<script src="user/dashboard/minerpayments.js"></script>
|
||||
<script src="user/dashboard/dashboard.js"></script>
|
||||
<script src="user/blocks/blocks.js"></script>
|
||||
<script src="user/payments/payments.js"></script>
|
||||
<script src="user/network/network.js"></script>
|
||||
<script src="user/ports/ports.js"></script>
|
||||
<script src="user/help/chat.js"></script>
|
||||
<script src="user/help/getting_started.js"></script>
|
||||
<script src="user/help/portsmodal.js"></script>
|
||||
<script src="user/help/faq.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
66
frontend/app/user/blocks/blocks.html
Normal file
66
frontend/app/user/blocks/blocks.html
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
<ng-include src="'user/dashboard/poolstats.html'"></ng-include>
|
||||
<div layout-md="column" layout="row" flex>
|
||||
<div layout="column" flex>
|
||||
<md-card>
|
||||
<md-toolbar class="md-table-toolbar md-default">
|
||||
<div class="md-toolbar-tools">
|
||||
<span>Blocks Found</span>
|
||||
</div>
|
||||
</md-toolbar>
|
||||
<md-card-content>
|
||||
<md-table-pagination md-limit="options.limit" md-limit-options="[15, 30, 50]" md-page="options.page" md-total="{{poolStats.global.totalBlocksFound}}" md-on-paginate="loadBlocks" md-page-select></md-table-pagination>
|
||||
<md-table-container id="blocksTable">
|
||||
<table md-table md-progress="promise">
|
||||
<thead md-head>
|
||||
<tr md-row>
|
||||
<th md-column><h2 class="md-subhead">Valid</h2></th>
|
||||
<th md-column><h2 class="md-subhead">Time Found</h2></th>
|
||||
<th md-column><h2 class="md-subhead">Height</h2></th>
|
||||
<th md-column><h2 class="md-subhead">Difficulty</h2></th>
|
||||
<th md-column><h2 class="md-subhead">Hash</h2></th>
|
||||
<th md-column><h2 class="md-subhead">Shares</h2></th>
|
||||
<th md-column><h2 class="md-subhead">Luck</h2></th>
|
||||
<th md-column><h2 class="md-subhead">Maturity</h2></th>
|
||||
<th md-column><h2 class="md-subhead">Pool</h2></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody md-body>
|
||||
<tr md-row ng-repeat="block in blocks.global">
|
||||
<td md-cell>
|
||||
<md-icon ng-class="{valid: block.valid, invalid: !block.valid}">
|
||||
{{::block.icon}}
|
||||
</md-icon>
|
||||
</td>
|
||||
<td md-cell>
|
||||
<h3 class="md-body-2" am-time-ago="block.ts"></h3>
|
||||
<md-tooltip>
|
||||
{{::block.ts | date:"hh:mm:ss dd/MM/yy"}}
|
||||
</md-tooltip>
|
||||
</td>
|
||||
<td md-cell><p class="md-body-2">{{::block.height | number}}</p></td>
|
||||
<td md-cell><p class="md-body-2">{{::block.diff | number}}</p></td>
|
||||
<td md-cell><p ng-bind-html="::block.hash | hashToLink: 'block'"></p></td>
|
||||
<td md-cell><p class="md-body-2">{{::block.shares | number}}</p></td>
|
||||
<td md-cell><p class="md-body-2">{{::block.luck | number:2}} %</p></td>
|
||||
<td md-cell>
|
||||
<md-progress-linear md-mode="query" ng-if="!block.maturity"></md-progress-linear>
|
||||
<p ng-if="block.maturity<0">
|
||||
<md-icon>lock_open</md-icon>
|
||||
<md-tooltip>
|
||||
{{-block.maturity}} blocks ago
|
||||
</md-tooltip>
|
||||
</p>
|
||||
<p ng-if="block.maturity>0">
|
||||
{{block.maturity}} to go
|
||||
</p>
|
||||
</td>
|
||||
<td md-cell><p class="md-body-2">{{::block.pool_type}}</p></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</md-table-container>
|
||||
<md-table-pagination md-limit="options.limit" md-limit-options="[15, 30, 50]" md-page="options.page" md-total="{{poolStats.global.totalBlocksFound}}" md-on-paginate="loadBlocks" md-page-select></md-table-pagination>
|
||||
</md-card-content>
|
||||
</md-card>
|
||||
</div>
|
||||
</div>
|
||||
47
frontend/app/user/blocks/blocks.js
Normal file
47
frontend/app/user/blocks/blocks.js
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
'use strict';
|
||||
|
||||
app.controller('BlocksCtrl', function($scope, $route, dataService, timerService) {
|
||||
$scope.blocks = {};
|
||||
$scope.selected = [];
|
||||
|
||||
$scope.options = {
|
||||
page: 1,
|
||||
limit: 15
|
||||
}
|
||||
|
||||
$scope.loadBlocks = function () {
|
||||
var params = angular.copy($scope.options);
|
||||
params.page -= 1;
|
||||
var urlParams = $.param(params)
|
||||
$scope.promise = dataService.getData("/pool/blocks?"+urlParams, function(data){
|
||||
$scope.blocks.global = data;
|
||||
updateMaturity();
|
||||
});
|
||||
};
|
||||
|
||||
var updateMaturity = function () {
|
||||
var luck;
|
||||
if($scope.poolStats.global !== undefined){
|
||||
_.each($scope.blocks.global, function(block, index){
|
||||
if($scope.network !== undefined) {
|
||||
$scope.blocks.global[index].maturity = $scope.config.maturity_depth - ($scope.network.height - block.height);
|
||||
}
|
||||
|
||||
// calculate luck
|
||||
luck = block.shares/block.diff*100;
|
||||
$scope.blocks.global[index].luck = (luck <= 100) ? (100-luck) : (-luck+100) ;
|
||||
$scope.blocks.global[index].icon = (block.valid) ? 'done' : 'clear';
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$scope.$watchGroup(["blocks.global", "poolStats.global"], updateMaturity);
|
||||
|
||||
// Register call with timer
|
||||
timerService.register($scope.loadBlocks, $route.current.controller);
|
||||
$scope.loadBlocks();
|
||||
|
||||
$scope.$on("$routeChangeStart", function () {
|
||||
timerService.remove($route.current.controller);
|
||||
});
|
||||
});
|
||||
240
frontend/app/user/dashboard/dashboard.html
Normal file
240
frontend/app/user/dashboard/dashboard.html
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
<ng-include src="'user/dashboard/poolstats.html'"></ng-include>
|
||||
<div layout="column" layout-lg="row" layout-gt-lg="row" flex>
|
||||
<div layout="column" flex-lg="30" flex-gt-lg="30">
|
||||
<md-card>
|
||||
<md-toolbar class="md-table-toolbar md-default">
|
||||
<div class="md-toolbar-tools">
|
||||
<span class="md-title">Network Stats</span></span>
|
||||
</div>
|
||||
</md-toolbar>
|
||||
<md-card-content>
|
||||
<md-list>
|
||||
<md-list-item>
|
||||
<p> Hash Rate </p>
|
||||
<p class="text-right">{{network.difficulty | difficultyToHashRate | toHashRate}}</p>
|
||||
</md-list-item>
|
||||
<md-list-item>
|
||||
<p> Difficulty </p>
|
||||
<p class="text-right">{{network.difficulty | number }}</p>
|
||||
</md-list-item>
|
||||
<md-list-item>
|
||||
<p> Hash </p>
|
||||
<p class="text-right truncate"><span ng-bind-html="network.hash | hashToLink: 'block'"></span></p>
|
||||
</md-list-item>
|
||||
<md-list-item>
|
||||
<p> Height </p>
|
||||
<p class="text-right">{{network.height | number }}</p>
|
||||
</md-list-item>
|
||||
<md-list-item>
|
||||
<p> Reward </p>
|
||||
<p class="text-right">{{network.value | toXMR}}</p>
|
||||
</md-list-item>
|
||||
<md-list-item>
|
||||
<p> Time Found </p>
|
||||
<p class="text-right"><span am-time-ago="network.ts * 1000"></span></p>
|
||||
</md-list-item>
|
||||
</md-list>
|
||||
</md-card-content>
|
||||
</md-card>
|
||||
</div>
|
||||
<div layout="column" flex-lg="70" flex-gt-lg="70">
|
||||
<md-card>
|
||||
<md-toolbar class="md-table-toolbar md-default">
|
||||
<div class="md-toolbar-tools">
|
||||
<span class="md-title">Pool Stats</span>
|
||||
</div>
|
||||
</md-toolbar>
|
||||
<md-card-content>
|
||||
<md-tabs md-dynamic-height>
|
||||
<md-tab label="{{pooltype}}" index="$index" ng-repeat="pooltype in poolList track by $index">
|
||||
<div layout-xs="column" layout-sm="column" layout="row" flex>
|
||||
<div layout-xs="row" layout-sm="row" layout="column" flex>
|
||||
<md-list flex>
|
||||
<md-list-item>
|
||||
<p> Hash Rate </p>
|
||||
<p class="text-right">{{ poolStats[pooltype].pool_statistics.hashRate | toHashRate }}</p>
|
||||
</md-list-item>
|
||||
<md-list-item>
|
||||
<p> Height </p>
|
||||
<p class="text-right">{{poolStats[pooltype].pool_statistics.lastBlockFound | number}}</p>
|
||||
</md-list-item>
|
||||
<md-list-item>
|
||||
<p> Last Block </p>
|
||||
<p class="text-right truncate"><span ng-bind-html="lastBlock[pooltype].hash | hashToLink: 'block'"></span></p>
|
||||
</md-list-item>
|
||||
<md-list-item>
|
||||
<p> Block Reward </p>
|
||||
<p class="text-right">{{lastBlock[pooltype].value | toXMR | number:10}} XMR</p>
|
||||
</md-list-item>
|
||||
<md-list-item>
|
||||
<p> Time Found </p>
|
||||
<p class="text-right">
|
||||
<span am-time-ago="poolStats[pooltype].pool_statistics.lastBlockFoundTime * 1000">Never</span>
|
||||
<md-tooltip>
|
||||
{{poolStats[pooltype].pool_statistics.lastBlockFoundTime*1000 | date:'hh:mm:ss dd/MM/yy'}}
|
||||
</md-tooltip>
|
||||
</p>
|
||||
</md-list-item>
|
||||
</md-list>
|
||||
</div>
|
||||
<div layout-xs="row" layout-sm="row" layout="column" flex>
|
||||
<md-list flex>
|
||||
<md-list-item>
|
||||
<p> Fees </p>
|
||||
<p class="text-right">{{ poolStats[pooltype].pool_statistics.fee }} %</p>
|
||||
</md-list-item>
|
||||
<md-list-item>
|
||||
<p> Blocks Found </p>
|
||||
<p class="text-right">{{ poolStats[pooltype].pool_statistics.totalBlocksFound || '0' }}</p>
|
||||
</md-list-item>
|
||||
<md-list-item>
|
||||
<p> Miners </p>
|
||||
<p class="text-right">{{ poolStats[pooltype].pool_statistics.miners || '0' }}</p>
|
||||
</md-list-item>
|
||||
<md-list-item>
|
||||
<p> Miners Paid </p>
|
||||
<p class="text-right">{{ poolStats[pooltype].pool_statistics.totalMinersPaid || '0' }}</p>
|
||||
</md-list-item>
|
||||
<md-list-item>
|
||||
<p> Payments sent </p>
|
||||
<p class="text-right">{{ poolStats[pooltype].pool_statistics.totalPayments || '0' }}</p>
|
||||
</md-list-item>
|
||||
</md-list>
|
||||
</div>
|
||||
</div>
|
||||
</md-tab>
|
||||
</md-tabs>
|
||||
</md-card-content>
|
||||
</md-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form layout layout-align="center" layout-padding>
|
||||
<div layout="row" flex>
|
||||
<md-input-container flex class="md-icon-float md-block md-title">
|
||||
<label>Enter Payment Address</label>
|
||||
<!-- below is the material icons -->
|
||||
<md-icon class="material-icons">account_balance_wallet</md-icon>
|
||||
<input type="text" ng-model="paymentAddress">
|
||||
</md-input-container>
|
||||
<div>
|
||||
<md-button class="md-raised md-primary" ng-click="addAddress()">
|
||||
<md-icon md-font-set="material-icons">add</md-icon>
|
||||
Track Live Stats
|
||||
</md-button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div layout-md="column" layout-sm="column" layout-xs="column" layout="row" flex ng-repeat="(addr,miner) in addrStats">
|
||||
<md-card flex>
|
||||
<md-toolbar class="md-accent">
|
||||
<div class="md-toolbar-tools">
|
||||
<md-input-container flex="60" class="md-icon-float md-block md-body-1 hide-error-msg">
|
||||
<label>Name your Miner Group</label>
|
||||
<!-- below is the material icons -->
|
||||
<md-icon class="material-icons">account_balance</md-icon>
|
||||
<input type="text" ng-model="miner.name">
|
||||
<md-tooltip>
|
||||
{{addr}}
|
||||
</md-tooltip>
|
||||
</md-input-container>
|
||||
<span class="md-caption">
|
||||
( Last Hash : <span am-time-ago="miner.lastHash * 1000">Never</span> )
|
||||
<md-tooltip>
|
||||
{{miner.lastHash*1000 | date:'hh:mm:ss dd/MM/yy'}}
|
||||
</md-tooltip>
|
||||
</span>
|
||||
<span flex></span>
|
||||
<span>
|
||||
<md-button class="md-fab md-mini" ng-click="deleteAddress(addr, $event)">
|
||||
<md-icon md-font-set="material-icons">clear</md-icon>
|
||||
</md-button>
|
||||
</span>
|
||||
</div>
|
||||
</md-toolbar>
|
||||
<md-card-content>
|
||||
<div flex layout="column" layout-md="row" layout-gt-md="row" class="text-center">
|
||||
<div flex layout="column" layout-gt-xs="row">
|
||||
<div flex>
|
||||
<h4> Hash Rate<h2>{{miner.hash | toHashRate}}</h2></h4>
|
||||
</div>
|
||||
<div flex>
|
||||
<h4> Total Hashes<h2>{{miner.totalHashes | number}}</h2></h4>
|
||||
</div>
|
||||
</div>
|
||||
<div flex layout="column" layout-gt-xs="row">
|
||||
<div flex>
|
||||
<h5> Total Due<h2>{{miner.amtDue | toXMR | number:10}} XMR</h2></h5>
|
||||
</div>
|
||||
<div flex>
|
||||
<h5> Total Paid<h2>{{miner.amtPaid | toXMR | number:10}} XMR</h2></h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</md-card-content>
|
||||
<md-card-content layout="column">
|
||||
<div class="chartcontainer" ng-if="minerStats[addr].dataset !== {}" flex>
|
||||
<linechart data="minerStats[addr].dataset" options="minerStats[addr].options"></linechart>
|
||||
</div>
|
||||
</md-card-content>
|
||||
<md-card-content>
|
||||
<div flex>
|
||||
<md-table-container>
|
||||
<table md-table md-row-select ng-model="minerStats[addr].table_selected" md-progress="minerStats[addr].promise" data-md-row-select="minerStats[addr].table_options.rowSelection" multiple="{{minerStats[addr].table_options.multiSelect}}">
|
||||
<thead md-head>
|
||||
<tr md-row>
|
||||
<th md-column md-numeric><h2 class="md-subhead">Worker</h2></th>
|
||||
<th md-column md-numeric><h2 class="md-subhead">#'s</h2></th>
|
||||
<th md-column md-numeric><h2 class="md-subhead">Total #'s</h2></th>
|
||||
<th md-column md-numeric><h2 class="md-subhead">Last Hash</h2></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody md-body>
|
||||
<tr md-row ng-repeat="(index, id) in addrStats[addr].ids" md-select="id" data-md-on-select="updateCharts" md-on-deselect="updateCharts">
|
||||
<td md-cell><p class="md-body-2">{{id}}</p></td>
|
||||
<td md-cell><p class="md-body-2">{{minerStats[addr].dataset[id][0].hs | toHashRate}}</p></td>
|
||||
<td md-cell><p class="md-body-2">{{addrStats[addr].workerStats[id].totalHash | number}}</p></td>
|
||||
<td md-cell>
|
||||
<span class="md-body-2" am-time-ago="minerStats[addr].dataset[id][0].ts"></span>
|
||||
<md-tooltip>
|
||||
{{minerStats[addr].dataset[id][0].ts | date: 'hh:mm:ss dd/MM/yy'}}
|
||||
</md-tooltip>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</md-table-container>
|
||||
</div>
|
||||
</md-card-content>
|
||||
<md-card-content>
|
||||
<div flex layout="column" layout-md="row" layout-gt-md="row" class="text-right">
|
||||
<div flex layout="column" layout-gt-xs="row">
|
||||
<div flex>
|
||||
|
||||
</div>
|
||||
<div flex>
|
||||
<h3> Valid Shares<h2>{{miner.validShares | number}} <md-icon class="valid">check</md-icon></h2></h3>
|
||||
<h3> Invalid Shares<h2>{{miner.invalidShares | number}} <md-icon class="invalid">clear</md-icon></h2></h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</md-card-content>
|
||||
</md-card-content>
|
||||
<md-card-actions layout="row" layout-align="end center" flex>
|
||||
<form layout layout-align="center" layout-padding>
|
||||
<div layout="row" flex>
|
||||
<md-checkbox ng-model="miner.alarm" ng-change="setAlarm(addr, miner.alarm)"></md-checkbox>
|
||||
<md-input-container flex class="md-icon-float md-block md-title">
|
||||
<label>Hashrate Alarm</label>
|
||||
<!-- below is the material icons -->
|
||||
<input type="text" ng-model="miner.alarmLimit">
|
||||
<md-icon class="material-icons">alarm</md-icon>
|
||||
</md-input-container>
|
||||
</div>
|
||||
</form>
|
||||
<span flex></span>
|
||||
<md-button class="md-raised md-primary" ng-click="viewPayments($event, miner, addr)">View Payments</md-button>
|
||||
</md-card-actions>
|
||||
</md-card>
|
||||
</div>
|
||||
110
frontend/app/user/dashboard/dashboard.js
Normal file
110
frontend/app/user/dashboard/dashboard.js
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
'use strict';
|
||||
|
||||
app.controller('DashboardCtrl', function($scope , $route, $mdDialog, $pageVisibility, dataService, timerService, addressService, minerService) {
|
||||
$scope.minerStats = {};
|
||||
|
||||
$scope.updateCharts = function (){
|
||||
minerService.updateStats($scope.addrStats, function(minerStats){
|
||||
$scope.minerStats = minerStats;
|
||||
});
|
||||
}
|
||||
|
||||
// Update miners everyime addrStats
|
||||
$scope.$parent.$watch('addrStats', function(newValue, oldValue) {
|
||||
$scope.updateCharts();
|
||||
});
|
||||
|
||||
$scope.addAddress = function (){
|
||||
if ($scope.paymentAddress){
|
||||
addressService.trackAddress($scope.paymentAddress);
|
||||
$scope.paymentAddress = "";
|
||||
}
|
||||
};
|
||||
|
||||
$scope.deleteAddress = function (key, ev){
|
||||
var confirm = $mdDialog.confirm()
|
||||
.title('Hide live stats?')
|
||||
.textContent('You can add it back by entering your wallet address again')
|
||||
.ariaLabel('Stop tracking payment address')
|
||||
.targetEvent(ev)
|
||||
.ok("Remove")
|
||||
.cancel("Cancel");
|
||||
|
||||
$mdDialog.show(confirm).then(function() {
|
||||
addressService.deleteAddress(key);
|
||||
}, function() {
|
||||
// cancel do nothing
|
||||
});
|
||||
}
|
||||
|
||||
$scope.setAlarm = function(addr, bool){
|
||||
addressService.setAlarm(addr, bool);
|
||||
};
|
||||
|
||||
$scope.viewPayments = function(ev, miner, addr){
|
||||
$mdDialog.show({
|
||||
locals: {
|
||||
miner: miner,
|
||||
addr: addr
|
||||
},
|
||||
controller: "MinerPaymentsCtrl",
|
||||
templateUrl: 'user/dashboard/minerpayments.html',
|
||||
parent: angular.element(document.body),
|
||||
targetEvent: ev,
|
||||
clickOutsideToClose:true,
|
||||
fullscreen: !$scope.menuOpen
|
||||
})
|
||||
.then(function(answer) {
|
||||
$scope.status = 'You said the information was "' + answer + '".';
|
||||
}, function() {
|
||||
$scope.status = 'You cancelled the dialog.';
|
||||
});
|
||||
}
|
||||
|
||||
// Recurring API calls and timer
|
||||
var loadData = function () {
|
||||
_.each($scope.poolList, function(pool_type) {
|
||||
dataService.getData("/pool/stats/"+pool_type, function(data){
|
||||
$scope.poolStats[pool_type] = data;
|
||||
});
|
||||
|
||||
dataService.getData("/pool/blocks/"+pool_type, function(data){
|
||||
if (data.length > 0){
|
||||
$scope.lastBlock[pool_type] = data[0];
|
||||
} else {
|
||||
$scope.lastBlock[pool_type] = {
|
||||
ts: 0,
|
||||
hash: "",
|
||||
diff: 0,
|
||||
shares: 0,
|
||||
height: 0,
|
||||
valid: false,
|
||||
unlocked: false,
|
||||
pool_type: pool_type,
|
||||
value: 0
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Call minerservice update
|
||||
$scope.updateCharts();
|
||||
};
|
||||
|
||||
// No spawn xhr reqs in bg
|
||||
$pageVisibility.$on('pageFocused', function(){
|
||||
minerService.runService(true);
|
||||
});
|
||||
|
||||
$pageVisibility.$on('pageBlurred', function(){
|
||||
minerService.runService(false);
|
||||
});
|
||||
|
||||
// Register call with timer
|
||||
timerService.register(loadData, $route.current.controller);
|
||||
loadData();
|
||||
|
||||
$scope.$on("$routeChangeStart", function () {
|
||||
timerService.remove($route.current.controller);
|
||||
});
|
||||
});
|
||||
54
frontend/app/user/dashboard/minerpayments.html
Normal file
54
frontend/app/user/dashboard/minerpayments.html
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
<md-dialog aria-label="Mango (Fruit)">
|
||||
<form ng-cloak>
|
||||
<md-toolbar>
|
||||
<div class="md-toolbar-tools">
|
||||
<h2>Payment History</h2>
|
||||
<span flex></span>
|
||||
<md-button class="md-icon-button" ng-click="answer('close')">
|
||||
<md-icon aria-label="Close dialog">clear</md-icon>
|
||||
</md-button>
|
||||
</div>
|
||||
</md-toolbar>
|
||||
<md-subheader>
|
||||
<p>To : <b class="truncate">{{addr}}</b></p>
|
||||
</md-subheader>
|
||||
<md-dialog-content>
|
||||
<md-table-pagination md-limit="options.limit" md-limit-options="[10, 20, 30]" md-page="options.page" md-total="{{miner.txnCount}}" md-on-paginate="loadPayments" md-page-select></md-table-pagination>
|
||||
<md-table-container>
|
||||
<table md-table md-row-select ng-model="selected" md-progress="promise">
|
||||
<thead md-head>
|
||||
<tr md-row>
|
||||
<th md-column md-numeric><h2 class="md-subhead">Time</h2></th>
|
||||
<th md-column md-numeric><h2 class="md-subhead">Type</h2></th>
|
||||
<th md-column md-numeric><h2 class="md-subhead">Amount</h2></th>
|
||||
<th md-column md-numeric><h2 class="md-subhead">Txn Hash</h2></th>
|
||||
<th md-column md-numeric><h2 class="md-subhead">Mixins</h2></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody md-body>
|
||||
<tr md-row ng-repeat="payment in payments">
|
||||
<td md-cell>
|
||||
<h3 class="md-body-2" am-time-ago="payment.ts*1000"></h3>
|
||||
<md-tooltip>
|
||||
{{payment.ts*1000 | date:'hh:mm:ss dd/MM/yy'}}
|
||||
</md-tooltip>
|
||||
</td>
|
||||
<td md-cell><p class="md-body-2">{{payment.pt}}</p></td>
|
||||
<td md-cell><p class="md-body-2">{{payment.amount | toXMR }} XMR</p></td>
|
||||
<td md-cell ng-bind-html="payment.txnHash | hashToLink: 'tx'"></td>
|
||||
<td md-cell><p class="md-body-2">{{payment.mixin}}</p></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</md-table-container>
|
||||
<md-table-pagination md-limit="options.limit" md-limit-options="[10, 20, 30]" md-page="options.page" md-total="{{miner.txnCount}}" md-on-paginate="loadPayments" md-page-select></md-table-pagination>
|
||||
</md-dialog-content>
|
||||
|
||||
<md-dialog-actions layout="row">
|
||||
<span flex></span>
|
||||
<md-button ng-click="answer('close')">
|
||||
Close
|
||||
</md-button>
|
||||
</md-dialog-actions>
|
||||
</form>
|
||||
</md-dialog>
|
||||
28
frontend/app/user/dashboard/minerpayments.js
Normal file
28
frontend/app/user/dashboard/minerpayments.js
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
'use strict';
|
||||
|
||||
app.controller('MinerPaymentsCtrl', function($scope, $mdDialog, dataService, miner, addr) {
|
||||
$scope.miner = miner;
|
||||
$scope.addr = addr;
|
||||
$scope.selected = [];
|
||||
|
||||
$scope.options = {
|
||||
page: 1,
|
||||
limit: 15
|
||||
}
|
||||
|
||||
$scope.loadPayments = function () {
|
||||
var params = angular.copy($scope.options);
|
||||
params.page -= 1;
|
||||
var urlParams = $.param(params)
|
||||
|
||||
dataService.getData("/miner/"+addr+"/payments?"+urlParams, function(data){
|
||||
$scope.payments = data;
|
||||
});
|
||||
}
|
||||
|
||||
$scope.loadPayments();
|
||||
|
||||
$scope.answer = function (answer) {
|
||||
$mdDialog.hide('close')
|
||||
}
|
||||
});
|
||||
72
frontend/app/user/dashboard/poolstats.html
Normal file
72
frontend/app/user/dashboard/poolstats.html
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
<div layout-gt-lg="row" layout="column" class="statsbar" flex>
|
||||
<div layout="column" layout-gt-sm="row" flex>
|
||||
<div layout="column" flex>
|
||||
<md-card class="metric">
|
||||
<md-card-title>
|
||||
<md-card-title-media>
|
||||
<div class="md-media-sm card-media" layout="row" layout-align="center center">
|
||||
<md-icon class="material-icons">multiline_chart</md-icon>
|
||||
</div>
|
||||
</md-card-title-media>
|
||||
<md-card-title-text>
|
||||
<span class="md-headline">{{poolStats.global.totalHashes | number}}</span>
|
||||
<span class="md-subhead">Hashes Accepted</span>
|
||||
</md-card-title-text>
|
||||
</md-card-title>
|
||||
</md-card>
|
||||
</div>
|
||||
<div layout="column" flex>
|
||||
<md-card class="metric">
|
||||
<md-card-title>
|
||||
<md-card-title-media>
|
||||
<div class="md-media-sm card-media" layout="row" layout-align="center center">
|
||||
<md-icon class="material-icons">widgets</md-icon>
|
||||
</div>
|
||||
</md-card-title-media>
|
||||
<md-card-title-text>
|
||||
<div class="md-headline">{{poolStats.global.totalBlocksFound}}</div>
|
||||
<div class="md-subhead">
|
||||
<span>Blocks Found (<span am-time-ago="poolStats.global.lastBlockFoundTime*1000"></span>)
|
||||
<md-tooltip md-direction="bottom">
|
||||
{{poolStats.global.lastBlockFoundTime*1000 | date:'hh:mm:ss dd/MM/yy'}}
|
||||
</md-tooltip>
|
||||
</div>
|
||||
</md-card-title-text>
|
||||
</md-card-title>
|
||||
</md-card>
|
||||
</div>
|
||||
</div>
|
||||
<div layout="column" layout-gt-sm="row" flex>
|
||||
<div layout="column" flex>
|
||||
<md-card class="metric">
|
||||
<md-card-title>
|
||||
<md-card-title-media>
|
||||
<div class="md-media-sm card-media" layout="row" layout-align="center center">
|
||||
<md-icon class="material-icons">memory</md-icon>
|
||||
</div>
|
||||
</md-card-title-media>
|
||||
<md-card-title-text>
|
||||
<span class="md-headline">{{poolStats.global.miners}}</span>
|
||||
<span class="md-subhead">Miners Connected</span>
|
||||
</md-card-title-text>
|
||||
</md-card-title>
|
||||
</md-card>
|
||||
</div>
|
||||
<div layout="column" flex>
|
||||
<md-card class="metric">
|
||||
<md-card-title>
|
||||
<md-card-title-media>
|
||||
<div class="md-media-sm card-media" layout="row" layout-align="center center">
|
||||
<md-icon class="material-icons">attach_money</md-icon>
|
||||
</div>
|
||||
</md-card-title-media>
|
||||
<md-card-title-text>
|
||||
<span class="md-headline">{{poolStats.global.totalMinersPaid}}</span>
|
||||
<span class="md-subhead">Miners Paid <a href="#/payments">({{poolStats.global.totalPayments}} Payments)</a></span>
|
||||
</md-card-title-text>
|
||||
|
||||
</md-card-title>
|
||||
</md-card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
16
frontend/app/user/help/chat.html
Normal file
16
frontend/app/user/help/chat.html
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<!-- <ng-include src="'dashboard/poolstats.html'"></ng-include> -->
|
||||
<div layout-sm="column" layout="row" flex>
|
||||
<div layout="column" flex>
|
||||
<md-card>
|
||||
<md-toolbar class="md-table-toolbar md-default">
|
||||
<div class="md-toolbar-tools">
|
||||
<span class="md-title">Support on #monero-pools <span class="md-caption">(Ask for help or say hello!)</span></span>
|
||||
</div>
|
||||
</md-toolbar>
|
||||
<md-card-content>
|
||||
<iframe src="https://kiwiirc.com/client/irc.kiwiirc.com/?&theme=mini#monero-pools" style="border:0; width:100%; height:650px;"></iframe>
|
||||
</div>
|
||||
</md-card-content>
|
||||
</md-card>
|
||||
</div>
|
||||
</div>
|
||||
4
frontend/app/user/help/chat.js
Normal file
4
frontend/app/user/help/chat.js
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
'use strict';
|
||||
|
||||
app.controller('ChatCtrl', function() {
|
||||
});
|
||||
33
frontend/app/user/help/faq.html
Normal file
33
frontend/app/user/help/faq.html
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
<div layout="column" layout-gt-md="row" flex>
|
||||
<md-card flex-gt-md="80" flex>
|
||||
<md-card-title>
|
||||
<span class="md-headline">Pool FAQ</span>
|
||||
</md-card-title>
|
||||
<md-card-content>
|
||||
<div ng-repeat="(topic,subject) in faq">
|
||||
<p class="md-title">{{topic}}</p>
|
||||
<div ng-repeat="(index, question) in subject" id="{{topic}}_{{index}}">
|
||||
<p class="md-subhead"><b>{{question.title}}</b></p>
|
||||
<p class="md-subhead" ng-bind-html="question.answer"></p>
|
||||
<div ng-if="question.media">
|
||||
<p class="md-subhead">{{question.media.title}}</p>
|
||||
<div class="text-center">
|
||||
<iframe width="75%" height="350px" ng-src="{{question.media.url}}" frameborder="0" allowfullscreen></iframe>
|
||||
<br/>
|
||||
</div>
|
||||
</div>
|
||||
<md-divider></md-divider>
|
||||
</div>
|
||||
</div>
|
||||
</md-card-content>
|
||||
</md-card>
|
||||
<md-card flex-gt-md="20" flex>
|
||||
<md-card-content class="fixed">
|
||||
<div class="md-headline">Contents</div>
|
||||
<div ng-repeat="(topic,subject) in faq">
|
||||
<p class="md-title">{{topic}}</p>
|
||||
<p class="md-subhead" ng-repeat="(index, q) in subject"><a class="theironfist" ng-click="goto(topic, index)">{{q.title}}</p>
|
||||
</div>
|
||||
</md-card-content>
|
||||
</md-card>
|
||||
</div>
|
||||
116
frontend/app/user/help/faq.js
Normal file
116
frontend/app/user/help/faq.js
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
'use strict';
|
||||
|
||||
app.controller('FAQCtrl', function($scope, $location, $anchorScroll, $sce, dataService) {
|
||||
// BooHoo:p
|
||||
$scope.goto = function(topic, index) {
|
||||
var newHash = topic + '_' + index;
|
||||
if ($location.hash() !== newHash) {
|
||||
// set the $location.hash to `newHash` and
|
||||
// $anchorScroll will automatically scroll to it
|
||||
$location.hash(newHash);
|
||||
} else {
|
||||
// call $anchorScroll() explicitly,
|
||||
// since $location.hash hasn't changed
|
||||
$anchorScroll();
|
||||
}
|
||||
};
|
||||
|
||||
$scope.faq = {
|
||||
"General" : [
|
||||
{
|
||||
title: "What is Monero?",
|
||||
answer: $sce.trustAsHtml("Monero is a cryptocurrency that promises untraceability and privacy. It accomplishes this by obfuscating and encrypting transactions beyond recognition, while allowing you to discreetly view and manage your assets. You can also prove your transactions to a third party if necessary.<br/>"),
|
||||
media:
|
||||
// {
|
||||
// "title": "Simple",
|
||||
// "url": $sce.trustAsResourceUrl("https://www.youtube.com/embed/TZi9xx6aiuY?ecver=1")
|
||||
// }
|
||||
{
|
||||
"title": "Monero essentials video",
|
||||
"url": $sce.trustAsResourceUrl("https://www.youtube.com/embed/6DQb0cMvU7I?ecver=1")
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
title: "What is mining and why should I be interested?",
|
||||
answer: $sce.trustAsHtml("Cryptocurrencies achieve decentralisation via a process called mining. When new transactions are created, they need to be validated. Miners compete with each other to validate a group of transactions(a.k.a. block). The winning miner is paid a block reward and collects transaction fees for the work carried out. Block rewards are also how new coins are generated and help regulate the economy of the currency.")
|
||||
},
|
||||
{
|
||||
title: "How do I start mining?",
|
||||
answer: $sce.trustAsHtml("You can start mining today if you have a computer that sits idle. Monero can be mined on CPUs, GPU's or even a raspberry PI. To start mining you need to find the right mining software for your hardware and get going.<br/><br/>Read <a hred='#/help/getting_started'>Getting Started</a> for more details.")
|
||||
},
|
||||
{
|
||||
title: "What is pool mining?",
|
||||
answer: $sce.trustAsHtml("If you are mining on a small scale, it becomes extremely hard and unpredictable to earn a stable profit on your mining income. Pool mining gives you the opportunity to join a group of miners and share earnings for a consistent payout.")
|
||||
},
|
||||
{
|
||||
title: "What is PPLNS?",
|
||||
answer: $sce.trustAsHtml("PPLNS is short for pay per last N shares. It is a method to split miner earnings fairly based on rounds. PPLNS hence favours loyal pool memebers over pool hoppers.")
|
||||
},
|
||||
{
|
||||
title: "What does SOLO mining mean?",
|
||||
answer: $sce.trustAsHtml("Solo mining is the opposite of pool mining. You essentially submit your shares directly to the blockchain, which is the most profitable method if you run your own farm.")
|
||||
},
|
||||
{
|
||||
title: "Is mining profitable?",
|
||||
answer: $sce.trustAsHtml("Mining can be profitable depending on the conditions involved. Your primary cost is your electricity and the cost of your hardware.<br/>It is not practical to calculate the exact amount you would earn as it depends on the total hash rate of the network, difficulty and your luck.<br/><br/>An accurate estimate of earnings of the pool can be calculated by observing average daily number of blocks found ... followed by some mathematics?<br/><br/>* An earnings estimator may be implemented in the future.")
|
||||
},
|
||||
],
|
||||
"Pool Help": [
|
||||
{
|
||||
title: "How payouts work?",
|
||||
answer: $sce.trustAsHtml("You might often wonder why you haven't been paid yet. This is normal and is how the payment cycle is designed to work.<br/><br/>Every 2 hours a master payment check is executed which pays out all dues. If everything goes as planned all dues that exceed the set payment thresholds are paid out. <br/><br/>In case of a wallet lock up or failure, the pool automatically requeues futher checks at 15 minute intervals until all payments are successfully completed. Once everything is paid out the system returns to the 2 hourly master cycle.<br/><br/> If you have any questions please dont hesitate to contact your pool admin.")
|
||||
},
|
||||
{
|
||||
title: "Payout thresholds?",
|
||||
answer: $sce.trustAsHtml("Payout threshold is the minimum amount that needs to be earned before the pool pays out to your wallet. Since transactions in Monero have a significant miner fees, it's cost effective to set a higher payout threshold for your pool. The minimum value for this is usually 0.3 XMR.<br/><br/>To change your payment threshold, click the wrench after you login via \"Login\" button on the top right.<br/><br/>You could also adjust your payout threshold to regulate your payout schedule etc daily/weekly etc depending on your hash rate.")
|
||||
},
|
||||
{
|
||||
title: "Why hasn't my \"Total Due\" amount increased?",
|
||||
answer: $sce.trustAsHtml("Sometimes, the monero blockchain will take a couple days for a new block to be found. Although you are contributing shares, the pool cannot guarantee your earnings until they are static.")
|
||||
},
|
||||
{
|
||||
title: "Getting paid in BTC",
|
||||
answer: $sce.trustAsHtml("nodejs-pool supports direct payments to btc. This is done by using the shapeshift API to convert your XMR and send them to a BTC wallet.<br/><br/>To configure BTC payments please have a look at <a href='#/help/getting_started'>Getting Started</a> command line samples.")
|
||||
},
|
||||
{
|
||||
title: "Payments to exchanges/markets?",
|
||||
answer: $sce.trustAsHtml("Direct payment to exchange / pool wallets are supported. The only primary difference when using this method is that the minimum payout threshold is higher and usually a defaults to 3XMR.")
|
||||
},
|
||||
{
|
||||
title: "IP Banning?",
|
||||
answer: $sce.trustAsHtml("Your IP gets banned if you submit invalid shares to the pool server. This usually happens if your card is overclocked or unstable.<br/><br/> The ban is temporary and usually cleared in xx mins. You could also contact your pool admin and request an unban.")
|
||||
},
|
||||
{
|
||||
title: "How Fixed / Variable Difficulty works",
|
||||
answer: $sce.trustAsHtml("When you select a pool port, the starting difficulty only represents your initial setting. As soon as your miner starts submitting shares the server will try to adjust your difficulty to reflect your hash rate.<br/><br/>This assures you not creating too many or too few requests to your server optimizing bandwidth consumption and server loads.<br/><br/>Optionally you could set a fixed difficulty via your miner command line options, though if you set a difficulty too high, you could exceed the 60 seconds job limit and loose earnings.")
|
||||
},
|
||||
{
|
||||
title: "Can i mine other coins?",
|
||||
answer: $sce.trustAsHtml("Not yet, but we may add more soon. Follow <a href='https://github.com/Snipa22/nodejs-pool/issues/27' target='_new'>https://github.com/Snipa22/nodejs-pool/issues/27</a>.")
|
||||
}
|
||||
],
|
||||
"Mining":[
|
||||
{
|
||||
title: "Hardware?",
|
||||
answer: $sce.trustAsHtml("Monero is an AISC resistant cryptocurrency, that means it should be cost prohibitive to mine monero with an FGPA/AISC allowing desktop grade hardware to keep its share in the network hashrate and earnings.<br/><br/><a href='http://monerobechmarks.byethost5.com/' target='_new'>http://monerobechmarks.byethost5.com/</a> is a list of community collected hashrate results ordered by hardware, but be careful as some entries may not be accurate.")
|
||||
},
|
||||
{
|
||||
title: "Software?",
|
||||
answer: $sce.trustAsHtml("Read -- <a href='#/help/getting_started'>Getting Started</a>.")
|
||||
}
|
||||
],
|
||||
"Support":[
|
||||
{
|
||||
title: "Chat Support",
|
||||
answer: $sce.trustAsHtml("Monero is an AISC resistant cryptocurrency, that means it should be cost prohibitive to mine monero with an FGPA/AISC allowing desktop grade hardware to keep its share in the network hashrate and earnings.<br/><br/><a href='http://monerobechmarks.byethost5.com/' target='_new'>http://monerobechmarks.byethost5.com/</a> is a list of community collected hashrate results ordered by hardware, but be careful as some entries may not be accurate.")
|
||||
},
|
||||
{
|
||||
title: "Interesting links.",
|
||||
answer: $sce.trustAsHtml("<a href='http://reddit.com/r/moneromining' target='_new'>http://reddit.com/r/moneromining/</a><br/><a href='http://monero.stackexchange.com/' target='_new'>http://monero.stackexchange.com/</a>")
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// end
|
||||
});
|
||||
98
frontend/app/user/help/getting_started.html
Normal file
98
frontend/app/user/help/getting_started.html
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
<div layout="row" flex>
|
||||
<div layout="column" flex>
|
||||
<md-card>
|
||||
<md-toolbar class="md-table-toolbar md-default">
|
||||
<div class="md-toolbar-tools">
|
||||
<span>Getting Started with {{GLOBALS.pool_name}}</span>
|
||||
</div>
|
||||
</md-toolbar>
|
||||
<md-card-content>
|
||||
<p class="panel-subtitle">If you are new to mining, this is the place for you.</p>
|
||||
<p class="panel-subtitle">take a deep breath, its only confusing at first but pure joy once up and running.</p>
|
||||
</md-card-content>
|
||||
</md-card>
|
||||
<md-card>
|
||||
<md-toolbar class="md-table-toolbar md-default">
|
||||
<div class="md-toolbar-tools">
|
||||
<span>1. Create a Wallet</span>
|
||||
</div>
|
||||
</md-toolbar>
|
||||
<md-card-content>
|
||||
<p class="panel-subtitle">Both online wallets and hardware wallets have their pros and cons.</p>
|
||||
<p class="panel-subtitle"><a href="https://www.mymonero.com" target="_blank">MyMonero</a> : MyMonero is an online wallet maintained by the core monero team, so while safe is still suceptable to hacks.</p>
|
||||
<p class="panel-subtitle"><a href="https://getmonero.org/2016/12/22/monero-core-gui-beta-released.html" target="_blank">Monero GUI Wallet</a> : The safest way to safely store and secure your XMR, although it would be your responsibility to kee your XMR Safe.</p>
|
||||
</md-card-content>
|
||||
</md-card>
|
||||
<md-card>
|
||||
<md-toolbar class="md-table-toolbar md-default">
|
||||
<div class="md-toolbar-tools">
|
||||
<span>2. Download mining software</span>
|
||||
</div>
|
||||
</md-toolbar>
|
||||
<md-card-content>
|
||||
<p class="panel-subtitle">AMD Miner</p>
|
||||
<p class="panel-subtitle">Nvidia Miner</p>
|
||||
<p class="panel-subtitle">CPU Miner (AESNI CPU's)</p>
|
||||
<p class="panel-subtitle">CPU Miner (Older CPU's})</p>
|
||||
</md-card-content>
|
||||
</md-card>
|
||||
<md-card>
|
||||
<md-toolbar class="md-table-toolbar md-default">
|
||||
<div class="md-toolbar-tools">
|
||||
<span>3. Pick a server and port</span>
|
||||
</div>
|
||||
</md-toolbar>
|
||||
<md-card-content>
|
||||
<p class="panel-subtitle">There are 2 important things here the <b>URL</b> and the <b>port</b> that you are going to connect to.</p>
|
||||
<p>You need to pick your URL and Port from the Ports List. Here you'll notice theres Global, PPLNS, Solo (<a href="#/help/faq">?</a>)</p>
|
||||
<p>Global is simply a collection of both PPLNS & Solo ports powerd by GEODNS. <br/>GEODNS automagically finds the closest server to you and could also be used to shares you in case of a regional outage.</p>
|
||||
<p>If you prefer to shave off a few of those ms and connect directly, you have the option to select your preferred endpoint too.</p>
|
||||
<p>The Port is used in conjunction with the url and is used to specify the starting difficulty. You should select this depending on the total #ing power of your rig (see port descriptions).</p>
|
||||
<p>If you are an advanced user or would like to set a fixed difficulty. examples below.</p>
|
||||
<div flex layout="row">
|
||||
<div flex></div>
|
||||
<md-button ng-click="viewPorts($event)">View Ports list</md-button>
|
||||
<div flex></div>
|
||||
</div>
|
||||
<!-- <p class="panel-subtitle">Curious what the port types are? Most miners are best served by PPLNS, which
|
||||
helps to combat pool hopping and ensures a good payout for miners. PPS is the most stable option of
|
||||
the lot, ensuring that you get paid out for every share, but with a higher fee for the risk the pool
|
||||
takes. Solo ports are for those who like to gamble and want to try and find their own block!</p> -->
|
||||
</md-card-content>
|
||||
</md-card>
|
||||
<md-card>
|
||||
<md-toolbar class="md-table-toolbar md-default">
|
||||
<div class="md-toolbar-tools">
|
||||
<span>The moment</span>
|
||||
</div>
|
||||
</md-toolbar>
|
||||
<md-card-content>
|
||||
<p class="panel-subtitle">OK So by now you should have a wallet, mining software, url and port. Its time to put them together.</p>
|
||||
</md-card-content>
|
||||
<md-card-content>
|
||||
<p>Here are some examples of the parameters that you need to pass on to your miner software, the format matters so be careful.</p>
|
||||
<p>Depending on your miner you need to add the required parameters via command line or config.txt</p>
|
||||
<p>Required fields are payment <b>address</b> and <b>MinerIdentifier</b></p>
|
||||
<blockquote>
|
||||
<i>Username format</i> : address.paymentID+FixedDifficulty<br/>
|
||||
<i>Password Format</i> : MinerIdentifier:Email<br/>
|
||||
</blockquote>
|
||||
<md-list flex>
|
||||
<md-subheader class="md-no-sticky">Username / Password Samples</md-subheader>
|
||||
<md-divider></md-divider>
|
||||
<md-list-item class="md-3-line" ng-repeat="sample in samples">
|
||||
<div class="md-list-item-text" layout="column">
|
||||
<h4>## {{ sample.desc }}</h4>
|
||||
<blockquote>
|
||||
<p><b>{{ sample.type }}</b> : {{ sample.sample }}</p>
|
||||
<small class="md-body-1">(e.g. miner.exe -u <i class="truncate">{{(sample.type=="Username") ? sample.sample : 'paymentAddress'}}</i> -p {{(sample.type=="Password") ? sample.sample : 'worker'}})</small><br/>
|
||||
</blockquote>
|
||||
</div>
|
||||
<md-divider></md-divider>
|
||||
</md-list-item>
|
||||
</md-list>
|
||||
</md-card-content>
|
||||
</md-card>
|
||||
<h3 class="text-center">Happy Mining !</h3>
|
||||
</div>
|
||||
</div>
|
||||
96
frontend/app/user/help/getting_started.js
Normal file
96
frontend/app/user/help/getting_started.js
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
'use strict';
|
||||
|
||||
app.controller('GettingStartedCtrl', function($scope, $mdDialog, dataService) {
|
||||
$scope.portsList = {};
|
||||
$scope.selected = [];
|
||||
|
||||
$scope.promise = dataService.getData("/pool/ports", function(data){
|
||||
$scope.portsList = data;
|
||||
});
|
||||
|
||||
$scope.viewPorts = function(ev){
|
||||
$mdDialog.show({
|
||||
controller: "PortsModalCtrl",
|
||||
templateUrl: 'user/help/portsmodal.html',
|
||||
parent: angular.element(document.body),
|
||||
targetEvent: ev,
|
||||
clickOutsideToClose:true,
|
||||
fullscreen: $scope.menuOpen // Only for -xs, -sm breakpoints.
|
||||
})
|
||||
.then(function(answer) {
|
||||
$scope.status = 'You said the information was "' + answer + '".';
|
||||
}, function() {
|
||||
$scope.status = 'You cancelled the dialog.';
|
||||
});
|
||||
}
|
||||
|
||||
$scope.samples=[
|
||||
{
|
||||
type: 'Username',
|
||||
sample: '43To46Y9AxNFkY5rsMQaLwbRNaxLZVvc4LJZt7Cx9Dt23frL6aut2uC3PsMiwGY5C5fKLSn6sWyoxRQTK1dhdBpKAX8bsUW',
|
||||
desc: 'Standard address for withdraws to a wallet (CLI/GUI/MyMonero)',
|
||||
valid: true
|
||||
},
|
||||
{
|
||||
type: 'Username',
|
||||
sample: '4DAU4uMdnDtFkY5rsMQaLwbRNaxLZVvc4LJZt7Cx9Dt23frL6aut2uC3PsMiwGY5C5fKLSn6sWyoxRQTK1dhdBpKF82nvn2H6jg9SUywAX',
|
||||
desc: 'Integrated address withdraw for exchange (TuxExchange)',
|
||||
valid: true
|
||||
},
|
||||
{
|
||||
type: 'Username',
|
||||
sample: '43To46Y9AxNFkY5rsMQaLwbRNaxLZVvc4LJZt7Cx9Dt23frL6aut2uC3PsMiwGY5C5fKLSn6sWyoxRQTK1dhdBpKAX8bsUW.6FEBAC2C05EDABB16E451D824894CC48AE8B645A48BD4C4F21A1CC8624EB0E6F',
|
||||
desc: 'Standard exchange withdrawl (Poloniex/etc.)',
|
||||
valid: true
|
||||
},
|
||||
{
|
||||
type: 'Username',
|
||||
sample: '1KEJ7EJvfD2bpL6vA9nJpTEgoS9P5jdyce',
|
||||
desc: 'BTC Withdrawal (Will process through xmr.to or shapeshift.io automatically)',
|
||||
valid: true
|
||||
},
|
||||
{
|
||||
type: 'Username',
|
||||
sample: '4DAU4uMdnDtFkY5rsMQaLwbRNaxLZVvc4LJZt7Cx9Dt23frL6aut2uC3PsMiwGY5C5fKLSn6sWyoxRQTK1dhdBpKF82nvn2H6jg9SUywAX.6FEBAC2C05EDABB16E451D824894CC48AE8B645A48BD4C4F21A1CC8624EB0E6F',
|
||||
desc: 'Integrated address withdraw for exchange w/ paymentID',
|
||||
valid: false
|
||||
},
|
||||
{
|
||||
type: 'Username',
|
||||
sample: '1KEJ7EJvfD2bpL6vA9nJpTEgoS9P5jdyce+100000',
|
||||
desc: 'BTC Withdrawal w/ fixed diff (Good for NiceHash)',
|
||||
valid: true
|
||||
},
|
||||
{
|
||||
type: 'Username',
|
||||
sample: '43To46Y9AxNFkY5rsMQaLwbRNaxLZVvc4LJZt7Cx9Dt23frL6aut2uC3PsMiwGY5C5fKLSn6sWyoxRQTK1dhdBpKAX8bsUW+3500',
|
||||
desc: 'Standard address for withdraws to a wallet w/ fixed diff (Good for NiceHash)',
|
||||
valid: true
|
||||
},
|
||||
{
|
||||
type: 'Username',
|
||||
sample: '43To46Y9AxNFkY5rsMQaLwbRNaxLZVvc4LJZt7Cx9Dt23frL6aut2uC3PsMiwGY5C5fKLSn6sWyoxRQTK1dhdBpKAX8bsUW.6FEBAC2C05EDABB16E451D824894CC48AE8B645A48BD4C4F21A1CC8624EB0E6F+23472',
|
||||
desc: 'Standard exchange withdrawl w/ fixed diff (Good for NiceHash)',
|
||||
valid: true
|
||||
},
|
||||
{
|
||||
type: 'Password',
|
||||
sample: 'Steve',
|
||||
desc: 'Miner identifier of Steve',
|
||||
valid: true
|
||||
},
|
||||
{
|
||||
type: 'Password',
|
||||
sample: 'x:test@e-mail.com',
|
||||
desc: 'Miner identifier of x, registers address + paymentID if there is one, to the e-mail address for notification',
|
||||
valid: true
|
||||
},
|
||||
{
|
||||
type: 'Password',
|
||||
sample: 'test@e-mail.com',
|
||||
desc: 'Will register the e-mail address as the worker ID',
|
||||
valid: true
|
||||
}
|
||||
]
|
||||
|
||||
});
|
||||
53
frontend/app/user/help/portsmodal.html
Normal file
53
frontend/app/user/help/portsmodal.html
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
<md-dialog aria-label="Mango (Fruit)">
|
||||
<form ng-cloak>
|
||||
<md-toolbar>
|
||||
<div class="md-toolbar-tools">
|
||||
<h2>Ports List</h2>
|
||||
<span flex></span>
|
||||
<md-button class="md-icon-button" ng-click="answer('close')">
|
||||
<md-icon aria-label="Close dialog">clear</md-icon>
|
||||
</md-button>
|
||||
</div>
|
||||
</md-toolbar>
|
||||
<md-dialog-content>
|
||||
<md-tabs md-dynamic-height>
|
||||
<md-tab label="{{pooltype}}" index="$index" ng-repeat="(pooltype, ports) in portsList">
|
||||
<md-table-container>
|
||||
<table md-table md-row-select ng-model="selected" md-progress="promise">
|
||||
<thead md-head md-order="portsOrder">
|
||||
<tr md-row>
|
||||
<th md-column md-numeric><h2 class="md-subhead">Server</h2></th>
|
||||
<th md-column md-numeric md-order-by="port"><h2 class="md-subhead">Port</h2></th>
|
||||
<th md-column md-numeric md-order-by="pool_type" ng-if="pooltype=='global'"><h2 class="md-subhead">Port Type</h2></th>
|
||||
<th md-column md-numeric md-order-by="difficulty"><h2 class="md-subhead">Difficulty</h2></th>
|
||||
<th md-column md-numeric md-order-by="miners"><h2 class="md-subhead">Miners</h2></th>
|
||||
<th md-column md-numeric><h2 class="md-subhead">Current Block ID</h2></th>
|
||||
<th md-column md-numeric><h2 class="md-subhead">Block Time</h2></th>
|
||||
<th md-column md-numeric><h2 class="md-subhead">Description</h2></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody md-body>
|
||||
<tr md-row ng-repeat="portinfo in ports | orderBy: portsOrder">
|
||||
<td md-cell><p class="md-body-2">{{portinfo.host.hostname}}</p></td>
|
||||
<td md-cell><p class="md-body-2">{{portinfo.port}}</p></td>
|
||||
<td md-cell ng-if="pooltype=='global'"><p class="md-body-2">{{portinfo.pool_type}}</p></td>
|
||||
<td md-cell><p class="md-body-2">{{portinfo.difficulty}}</p></td>
|
||||
<td md-cell><p class="md-body-2">{{portinfo.miners}}</p></td>
|
||||
<td md-cell><p class="md-body-2">{{portinfo.host.blockID}}</p></td>
|
||||
<td md-cell><p class="md-body-2">{{portinfo.host.blockIDTime * 1000 | date: 'hh:mm:ss - dd/MM/yy'}}</p></td>
|
||||
<td md-cell><p class="md-body-2">{{portinfo.description}}</p></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</md-table-container>
|
||||
</md-tab>
|
||||
</md-card-content>
|
||||
</md-dialog-content>
|
||||
<md-dialog-actions layout="row">
|
||||
<span flex></span>
|
||||
<md-button ng-click="answer('close')">
|
||||
Close
|
||||
</md-button>
|
||||
</md-dialog-actions>
|
||||
</form>
|
||||
</md-dialog>
|
||||
13
frontend/app/user/help/portsmodal.js
Normal file
13
frontend/app/user/help/portsmodal.js
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
'use strict';
|
||||
|
||||
app.controller('PortsModalCtrl', function($scope, $mdDialog, dataService) {
|
||||
$scope.selected = [];
|
||||
|
||||
$scope.promise = dataService.getData("/pool/ports", function(data){
|
||||
$scope.portsList = data;
|
||||
});
|
||||
|
||||
$scope.answer = function (answer) {
|
||||
$mdDialog.hide('close')
|
||||
}
|
||||
});
|
||||
73
frontend/app/user/home/console.html
Normal file
73
frontend/app/user/home/console.html
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
<md-dialog aria-label="Miner Login" >
|
||||
<form ng-cloak>
|
||||
<md-toolbar>
|
||||
<div class="md-toolbar-tools">
|
||||
<h2>Miner Console</h2>
|
||||
<span flex></span>
|
||||
<md-button class="md-icon-button" ng-click="cancel()">
|
||||
<md-icon aria-label="Close dialog">clear</md-icon>
|
||||
</md-button>
|
||||
</div>
|
||||
</md-toolbar>
|
||||
|
||||
<md-dialog-content>
|
||||
<md-tabs class="console">
|
||||
<md-tab label="Payment Threshold" md-on-select="currentTab = 'Threshold'">
|
||||
<div layout-padding>
|
||||
<p class="" ng-if="paymentThresh==0">You currently have no payment threshold set, defaults are in use.</p>
|
||||
<form name="thresholdForm" autocomplete="off">
|
||||
<md-input-container>
|
||||
<label>Payment Threshold Limit</label>
|
||||
<input type="number" ng-model="paymentThresh" name="threshold" step="any" required min="{{min_wallet_payout | toXMR}}">
|
||||
</md-input-container>
|
||||
<div ng-messages="thresholdForm.threshold.$error" multiple>
|
||||
<div ng-message="min">
|
||||
This value cannot be lower than {{min_wallet_payout | toXMR}} XMR
|
||||
</div>
|
||||
</div>
|
||||
<p>The pool will pay out to your wallet once your total due exceeds the payment threshold</p>
|
||||
</form>
|
||||
</div>
|
||||
</md-tab>
|
||||
<md-tab label="Email Settings" md-on-select="currentTab = 'Email'">
|
||||
<div layout-padding>
|
||||
<form name="emailForm" autocomplete="off">
|
||||
<md-checkbox ng-model="email_toggle" ng-true-value="1" ng-false-value="0">Receive miner hashrate alerts</md-checkbox>
|
||||
</form>
|
||||
</div>
|
||||
</md-tab>
|
||||
|
||||
<md-tab label="Change Password" md-on-select="currentTab = 'Password'">
|
||||
<div layout-padding>
|
||||
<form name="passwordForm" autocomplete="off">
|
||||
<md-input-container class="md-block">
|
||||
<label>New Password</label>
|
||||
<input type="password" ng-model="password.pwd" name="pwd" required md-autofocus>
|
||||
</md-input-container>
|
||||
<md-input-container class="md-block">
|
||||
<label>Confirm Password</label>
|
||||
<input type="password" id="cnfpwd" ng-model="password.cnf" name="cnf" required compare-to="password.pwd" ng-focus="status == 'Check passwords'">
|
||||
<div ng-messages="passwordForm.cnf.$error" multiple>
|
||||
<div ng-message="compare-to">
|
||||
Please confirm your new password
|
||||
</div>
|
||||
</div>
|
||||
</md-input-container>
|
||||
</form>
|
||||
</div>
|
||||
</md-tab>
|
||||
</md-tabs>
|
||||
</md-dialog-content>
|
||||
|
||||
<md-dialog-actions layout="row">
|
||||
<md-button class="md-raised md-primary md-warn" ng-click="logout()">
|
||||
Logout
|
||||
</md-button>
|
||||
<span flex></span>
|
||||
<p ng-class="statusClass">{{status}}</p>
|
||||
<md-button class="md-primary" ng-click="save()">
|
||||
Update {{currentTab}}
|
||||
</md-button>
|
||||
</md-dialog-actions>
|
||||
</form>
|
||||
</md-dialog>
|
||||
95
frontend/app/user/home/console.js
Normal file
95
frontend/app/user/home/console.js
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
'use strict';
|
||||
|
||||
app.controller('ConsoleCtrl', function($scope, $route, $filter, $timeout, $mdDialog, min_wallet_payout, dataService, timerService) {
|
||||
$scope.paymentThresh;
|
||||
$scope.min_wallet_payout = min_wallet_payout;
|
||||
$scope.currentTab = 'Threshold'; // default tab
|
||||
$scope.status = "";
|
||||
$scope.statusClass = "valid";
|
||||
$scope.password = {
|
||||
pwd: "",
|
||||
cnf: ""
|
||||
}
|
||||
|
||||
var email_enabled;
|
||||
|
||||
var getConfig = function () {
|
||||
dataService.getData("/authed", function(data){
|
||||
$scope.paymentThresh = $filter('toXMR')(data.msg.payout_threshold);
|
||||
email_enabled = data.msg.email_enabled;
|
||||
$scope.email_toggle = data.msg.email_enabled;
|
||||
});
|
||||
}
|
||||
|
||||
var updateThreshold = function () {
|
||||
dataService.postData("/authed/changePayoutThreshold", {threshold: $scope.paymentThresh},function(data){
|
||||
//$mdDialog.hide('updated');
|
||||
$scope.statusClass = "valid";
|
||||
$scope.status = "Threshold Saved";
|
||||
messageFlash();
|
||||
});
|
||||
}
|
||||
|
||||
var updatePassword = function () {
|
||||
if($scope.password.pwd == $scope.password.cnf && $scope.password.pwd !== "") {
|
||||
dataService.postData("/authed/changePassword", {password: $scope.password.pwd},function(data){
|
||||
//$mdDialog.hide('updated');
|
||||
$scope.statusClass = "valid";
|
||||
$scope.status = "Password Saved";
|
||||
messageFlash();
|
||||
});
|
||||
} else {
|
||||
$scope.statusClass = "invalid";
|
||||
$scope.status = "Check passwords";
|
||||
messageFlash();
|
||||
}
|
||||
}
|
||||
|
||||
var updateEmail = function () {
|
||||
if($scope.email_toggle!=email_enabled){
|
||||
dataService.postData("/authed/toggleEmail", {}, function(data) {
|
||||
$scope.status = data.msg;
|
||||
email_enabled=$scope.email_toggle;
|
||||
messageFlash();
|
||||
});
|
||||
} else {
|
||||
$scope.statusClass = "invalid";
|
||||
$scope.status = "No Change...";
|
||||
messageFlash();
|
||||
}
|
||||
}
|
||||
|
||||
var messageFlash = function(){
|
||||
$timeout(function() {
|
||||
$scope.status = "";
|
||||
$scope.statusClass = "valid";
|
||||
},2000);
|
||||
}
|
||||
|
||||
$scope.save = function () {
|
||||
$scope.statusClass = "valid";
|
||||
$scope.status = "Saving...";// + $scope.currentTab;
|
||||
switch ($scope.currentTab){
|
||||
case 'Threshold':
|
||||
updateThreshold();
|
||||
break;
|
||||
|
||||
case 'Password':
|
||||
updatePassword();
|
||||
|
||||
case 'Email':
|
||||
updateEmail();
|
||||
}
|
||||
}
|
||||
|
||||
$scope.logout = function () {
|
||||
$mdDialog.hide('logout');
|
||||
}
|
||||
|
||||
getConfig();
|
||||
|
||||
// Dialog methods
|
||||
$scope.cancel = function () {
|
||||
$mdDialog.cancel();
|
||||
}
|
||||
});
|
||||
69
frontend/app/user/home/home.html
Normal file
69
frontend/app/user/home/home.html
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
<ng-include src="'user/dashboard/poolstats.html'"></ng-include>
|
||||
<div layout="column" layout-gt-md="row" flex>
|
||||
<md-card flex ng-include="'welcome.html'">
|
||||
</md-card>
|
||||
<div flex flex-gt-md="30">
|
||||
<md-card flex>
|
||||
<md-card-title>
|
||||
<span class="md-headline">Pool Config</span>
|
||||
</md-card-title>
|
||||
<md-card-content>
|
||||
<md-list>
|
||||
<!-- <md-list-item>
|
||||
<p>PPS Fee</p>
|
||||
<span class="text-right">
|
||||
{{config.pps_fee}}%
|
||||
<md-tooltip>
|
||||
Core Devs Donation: {{config.dev_donation/100*config.pps_fee | number:3 }}% |
|
||||
Pool Devs Donation:{{config.pool_dev_donation/100*config.pps_fee | number:3 }}%
|
||||
</md-tooltip>
|
||||
</span>
|
||||
</md-list-item> -->
|
||||
<md-list-item>
|
||||
<p>PPLNS Fee</p>
|
||||
<span class="text-right">
|
||||
{{config.pplns_fee}}%
|
||||
<md-tooltip>
|
||||
Core Devs Donation: {{config.dev_donation/100*config.pplns_fee | number:3 }}% |
|
||||
Pool Devs Donation:{{config.pool_dev_donation/100*config.pplns_fee | number:3 }}%
|
||||
</md-tooltip>
|
||||
</span>
|
||||
</md-list-item>
|
||||
<md-list-item>
|
||||
<p>Solo Fee</p>
|
||||
<p class="text-right">
|
||||
{{config.solo_fee}}%
|
||||
<md-tooltip>
|
||||
Core Devs Donation: {{config.dev_donation/100*config.solo_fee | number:3}}% |
|
||||
Pool Devs Donation:{{config.pool_dev_donation/100*config.solo_fee | number:3}}%
|
||||
</md-tooltip>
|
||||
</p>
|
||||
</md-list-item>
|
||||
<md-list-item>
|
||||
<p>BTC Fee</p>
|
||||
<p class="text-right">{{config.btc_fee}}%</p>
|
||||
</md-list-item>
|
||||
<md-list-item>
|
||||
<p>Minimum payout (Wallet)</p>
|
||||
<p class="text-right">{{config.min_wallet_payout | toXMR}} XMR</p>
|
||||
</md-list-item>
|
||||
<md-list-item>
|
||||
<p>Minimum payout (BTC)</p>
|
||||
<p class="text-right">{{config.min_btc_payout | toXMR}} XMR</p>
|
||||
</md-list-item>
|
||||
<md-list-item>
|
||||
<p>Minimum payout (Exchange)</p>
|
||||
<p class="text-right">{{config.min_exchange_payout | toXMR}} XMR</p>
|
||||
</md-list-item>
|
||||
<md-list-item>
|
||||
<p>Block Maturity Depth</p>
|
||||
<p class="text-right">{{config.maturity_depth}}</p>
|
||||
</md-list-item>
|
||||
<md-list-item>
|
||||
<p>Minimum Denomination</p>
|
||||
<p class="text-right">{{config.min_denom | toXMR}}</p>
|
||||
</md-list-item>
|
||||
</md-list>
|
||||
</md-card-content>
|
||||
</md-card></div>
|
||||
</div>
|
||||
5
frontend/app/user/home/home.js
Normal file
5
frontend/app/user/home/home.js
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
'use strict';
|
||||
|
||||
app.controller('HomeCtrl', function($scope, $route, dataService, timerService) {
|
||||
|
||||
});
|
||||
43
frontend/app/user/home/login.html
Normal file
43
frontend/app/user/home/login.html
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<md-dialog aria-label="Miner Login">
|
||||
<md-content class="login">
|
||||
<md-toolbar>
|
||||
<div class="md-toolbar-tools">
|
||||
<h2>Miner Login</h2>
|
||||
<span flex></span>
|
||||
<md-button class="md-icon-button" ng-click="cancel()">
|
||||
<md-icon aria-label="Close dialog">clear</md-icon>
|
||||
</md-button>
|
||||
</div>
|
||||
</md-toolbar>
|
||||
|
||||
<md-dialog-content layout-padding>
|
||||
<form name="loginForm">
|
||||
<div>
|
||||
<md-input-container class="md-icon-float md-block">
|
||||
<!-- Use floating label instead of placeholder -->
|
||||
<label>Payment Address</label>
|
||||
<md-icon>person</md-icon>
|
||||
<input ng-model="user.username" name="username" type="text" md-autofocus>
|
||||
</md-input-container>
|
||||
|
||||
<md-input-container class="md-icon-float md-block">
|
||||
<label>Password</label>
|
||||
<md-icon>lock</md-icon>
|
||||
<input ng-model="user.password" name="password" type="password" placeholder="your email identifier (read Getting Started to set one)">
|
||||
</md-input-container>
|
||||
</div>
|
||||
</form>
|
||||
</md-dialog-content>
|
||||
|
||||
<md-dialog-actions layout="row">
|
||||
<md-checkbox class="md-primary" ng-model="remember">
|
||||
Remember me
|
||||
</md-checkbox>
|
||||
<span flex></span>
|
||||
<p class="invalid">{{status}}</p>
|
||||
<md-button class="md-raised md-primary" ng-click="login()">
|
||||
Login
|
||||
</md-button>
|
||||
</md-dialog-actions>
|
||||
</md-content>
|
||||
</md-dialog>
|
||||
29
frontend/app/user/home/login.js
Normal file
29
frontend/app/user/home/login.js
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
'use strict';
|
||||
|
||||
app.controller('LoginCtrl', function($scope, $route, $mdDialog, dataService, timerService) {
|
||||
$scope.user = {
|
||||
username: "",
|
||||
password: ""
|
||||
}
|
||||
|
||||
$scope.remember = false;
|
||||
$scope.status = "";
|
||||
|
||||
$scope.login = function () {
|
||||
dataService.postData("/authenticate", $scope.user, function(data){
|
||||
if (data.success){
|
||||
data.remember = $scope.remember;
|
||||
dataService.setAuthToken(data);
|
||||
$mdDialog.hide(data);
|
||||
} else {
|
||||
$mdDialog.hide(false);
|
||||
}
|
||||
}, function(error){
|
||||
$scope.status = "Please check your login details";
|
||||
});
|
||||
}
|
||||
|
||||
$scope.cancel = function () {
|
||||
$mdDialog.cancel();
|
||||
}
|
||||
});
|
||||
5
frontend/app/user/network/network.html
Normal file
5
frontend/app/user/network/network.html
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<h1>Coming SoOn..</h1>
|
||||
</div>
|
||||
</div>
|
||||
22
frontend/app/user/network/network.js
Normal file
22
frontend/app/user/network/network.js
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
'use strict';
|
||||
|
||||
app.controller('NetworkCtrl', function($scope, $route, dataService, timerService) {
|
||||
|
||||
|
||||
var loadData = function () {
|
||||
console.log("Getting Network Data");
|
||||
|
||||
dataService.getData("/network/chart/usdHash/60", function(data){
|
||||
$scope.config = data;
|
||||
console.log(data);
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
loadData();
|
||||
// timerService.register(loadData, $route.current.controller);
|
||||
|
||||
// $scope.$on("$routeChangeStart", function () {
|
||||
// timerService.remove($route.current.controller);
|
||||
// });
|
||||
});
|
||||
45
frontend/app/user/payments/payments.html
Normal file
45
frontend/app/user/payments/payments.html
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
<ng-include src="'user/dashboard/poolstats.html'"></ng-include>
|
||||
<div layout-sm="column" layout="row" flex>
|
||||
<div layout="column" flex>
|
||||
<md-card>
|
||||
<md-toolbar class="md-table-toolbar md-default">
|
||||
<div class="md-toolbar-tools">
|
||||
<span>Payments Made</span>
|
||||
</div>
|
||||
</md-toolbar>
|
||||
<md-card-content>
|
||||
<md-table-pagination md-limit="options.limit" md-limit-options="[15, 30, 50]" md-page="options.page" md-total="{{poolStats.global.totalPayments}}" md-on-paginate="loadPayments" md-page-select></md-table-pagination>
|
||||
<md-table-container>
|
||||
<table md-table md-row-select ng-model="selected" md-progress="promise">
|
||||
<thead md-head>
|
||||
<tr md-row>
|
||||
<th md-column><h2 class="md-subhead">Time Sent</h2></th>
|
||||
<th md-column><h2 class="md-subhead">Transaction Hash</h2></th>
|
||||
<th md-column><h2 class="md-subhead">Amount</h2></th>
|
||||
<th md-column><h2 class="md-subhead">Fee</h2></th>
|
||||
<th md-column><h2 class="md-subhead">Mixin</h2></th>
|
||||
<th md-column><h2 class="md-subhead">Payees</h2></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody md-body>
|
||||
<tr md-row ng-repeat="payment in payments.global">
|
||||
<td md-cell>
|
||||
<h3 class="md-body-2" am-time-ago="payment.ts"></h3>
|
||||
<md-tooltip>
|
||||
{{payment.ts | date:'hh:mm:ss dd/MM/yy'}}
|
||||
</md-tooltip>
|
||||
</td>
|
||||
<td md-cell ng-bind-html="payment.hash | hashToLink: 'tx'"></td>
|
||||
<td md-cell><p class="md-body-2">{{payment.value | toXMR}} XMR</p></td>
|
||||
<td md-cell><p class="md-body-2">{{payment.fee | toXMR }} XMR</p></td>
|
||||
<td md-cell><p class="md-body-2">{{payment.mixins}}</p></td>
|
||||
<td md-cell><p class="md-body-2">{{payment.payees}}</p></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</md-table-container>
|
||||
<md-table-pagination md-limit="options.limit" md-limit-options="[15, 30, 50]" md-page="options.page" md-total="{{poolStats.global.totalPayments}}" md-on-paginate="loadPayments" md-page-select></md-table-pagination>
|
||||
</md-card-content>
|
||||
</md-card>
|
||||
</div>
|
||||
</div>
|
||||
24
frontend/app/user/payments/payments.js
Normal file
24
frontend/app/user/payments/payments.js
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
'use strict';
|
||||
|
||||
app.controller('PaymentsCtrl', function($scope, dataService) {
|
||||
$scope.payments = {};
|
||||
$scope.selected = [];
|
||||
|
||||
$scope.options = {
|
||||
page: 1,
|
||||
limit: 15
|
||||
}
|
||||
|
||||
$scope.loadPayments = function () {
|
||||
var params = angular.copy($scope.options);
|
||||
params.page -= 1;
|
||||
var urlParams = $.param(params)
|
||||
|
||||
dataService.getData("/pool/payments?"+urlParams, function(data){
|
||||
$scope.payments.global = data;
|
||||
});
|
||||
}
|
||||
|
||||
$scope.loadPayments();
|
||||
|
||||
});
|
||||
42
frontend/app/user/ports/ports.html
Normal file
42
frontend/app/user/ports/ports.html
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<ng-include src="'user/dashboard/poolstats.html'"></ng-include>
|
||||
<md-card>
|
||||
<md-toolbar class="md-table-toolbar md-default">
|
||||
<div class="md-toolbar-tools">
|
||||
<span class="md-title">Ports List</span>
|
||||
</div>
|
||||
</md-toolbar>
|
||||
<md-card-content>
|
||||
<md-tabs md-dynamic-height>
|
||||
<md-tab label="{{pooltype}}" index="$index" ng-repeat="(pooltype, ports) in portsList">
|
||||
<md-table-container>
|
||||
<table md-table md-row-select ng-model="selected" md-progress="promise">
|
||||
<thead md-head md-order="portsOrder">
|
||||
<tr md-row>
|
||||
<th md-column md-numeric><h2 class="md-subhead">Server</h2></th>
|
||||
<th md-column md-numeric md-order-by="port"><h2 class="md-subhead">Port</h2></th>
|
||||
<th md-column md-numeric md-order-by="pool_type" ng-if="pooltype=='global'"><h2 class="md-subhead">Port Type</h2></th>
|
||||
<th md-column md-numeric md-order-by="difficulty"><h2 class="md-subhead">Difficulty</h2></th>
|
||||
<th md-column md-numeric md-order-by="miners"><h2 class="md-subhead">Miners</h2></th>
|
||||
<th md-column md-numeric><h2 class="md-subhead">Current Block ID</h2></th>
|
||||
<th md-column md-numeric><h2 class="md-subhead">Block Time</h2></th>
|
||||
<th md-column md-numeric><h2 class="md-subhead">Description</h2></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody md-body>
|
||||
<tr md-row ng-repeat="portinfo in ports | orderBy: portsOrder">
|
||||
<td md-cell><p class="md-body-2">{{portinfo.host.hostname}}</p></td>
|
||||
<td md-cell><p class="md-body-2">{{portinfo.port}}</p></td>
|
||||
<td md-cell ng-if="pooltype=='global'"><p class="md-body-2">{{portinfo.pool_type}}</p></td>
|
||||
<td md-cell><p class="md-body-2">{{portinfo.difficulty}}</p></td>
|
||||
<td md-cell><p class="md-body-2">{{portinfo.miners}}</p></td>
|
||||
<td md-cell><p class="md-body-2">{{portinfo.host.blockID}}</p></td>
|
||||
<td md-cell><p class="md-body-2">{{portinfo.host.blockIDTime * 1000 | date: 'hh:mm:ss - dd/MM/yy'}}</p></td>
|
||||
<td md-cell><p class="md-body-2">{{portinfo.description}}</p></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</md-table-container>
|
||||
</md-tab>
|
||||
</md-tabs>
|
||||
</md-card-content>
|
||||
</md-card>
|
||||
10
frontend/app/user/ports/ports.js
Normal file
10
frontend/app/user/ports/ports.js
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
'use strict';
|
||||
|
||||
app.controller('PortsCtrl', function($scope, $route, dataService, timerService) {
|
||||
$scope.portsList = {};
|
||||
$scope.selected = [];
|
||||
|
||||
$scope.promise = dataService.getData("/pool/ports", function(data){
|
||||
$scope.portsList = data;
|
||||
});
|
||||
});
|
||||
79
frontend/app/utils/dataservice.js
Normal file
79
frontend/app/utils/dataservice.js
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
angular.module('utils.xhr', [])
|
||||
.service('dataService', function($http, $localStorage, $sessionStorage, GLOBALS) {
|
||||
var apiURL = GLOBALS.api_url;
|
||||
var sessStorage = $sessionStorage;
|
||||
var storage = $localStorage;
|
||||
var sessionLock = false;
|
||||
|
||||
this.getData = function(url, successFn, errorFn) {
|
||||
this.xhr('GET', url, {}, successFn, errorFn);
|
||||
}
|
||||
|
||||
this.postData = function(url, params, successFn, errorFn) {
|
||||
this.xhr('POST', url, params, successFn, errorFn);
|
||||
}
|
||||
|
||||
this.putData = function(url, params, successFn, errorFn) {
|
||||
this.xhr('PUT', url, params, successFn, errorFn);
|
||||
}
|
||||
|
||||
this.deleteData = function(url, params, successFn, errorFn) {
|
||||
this.xhr('DELETE', url, params, successFn, errorFn);
|
||||
}
|
||||
|
||||
this.xhr = function (type, url, params, successFn, errorFn) {
|
||||
$http({
|
||||
method: type,
|
||||
url: apiURL + url,
|
||||
data: params,
|
||||
headers: this.getRequestHeaders()
|
||||
}).then(function successCallback(response) {
|
||||
successFn(response.data);
|
||||
}, function errorCallback(response) {
|
||||
if (errorFn && response != undefined) errorFn(response); else console.log("Network Error", response);
|
||||
}).$promise;
|
||||
}
|
||||
|
||||
this.setAuthToken = function(token) {
|
||||
sessStorage.token = token.msg;
|
||||
storage.authToken = (token.remember) ? token.msg : false; // remember me
|
||||
this.validateSession();
|
||||
}
|
||||
|
||||
this.getRequestHeaders = function() {
|
||||
this.validateSession();
|
||||
return { 'x-access-token': (sessStorage.token) ? sessStorage.token : "" };
|
||||
}
|
||||
|
||||
this.isLoggedIn = function() {
|
||||
return sessStorage.token || storage.authToken;
|
||||
}
|
||||
|
||||
this.validateSession = function () {
|
||||
if (storage.authToken !== undefined){
|
||||
sessionLock = true;
|
||||
if (storage.authToken) {
|
||||
$http.defaults.headers.common['x-access-token'] = storage.authToken;
|
||||
sessStorage.token = storage.authToken;
|
||||
}
|
||||
} else if (sessionLock) {
|
||||
// logout if, logout detected on another browser session
|
||||
this.logout();
|
||||
sessionLock=false;
|
||||
}
|
||||
}
|
||||
|
||||
this.logout = function() {
|
||||
// invalidate existing token
|
||||
$http.get(apiURL+"/authed/tokenRefresh")
|
||||
.then(function (data) {
|
||||
/* Do nothing */
|
||||
}, function (err) {
|
||||
console.log("debug", err);
|
||||
});
|
||||
delete storage.authToken;
|
||||
delete sessStorage.authToken;
|
||||
delete sessStorage.token;
|
||||
// invalidate token on server todo
|
||||
}
|
||||
})
|
||||
21
frontend/app/utils/directives.js
Normal file
21
frontend/app/utils/directives.js
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
var compareTo = function() {
|
||||
return {
|
||||
require: "ngModel",
|
||||
scope: {
|
||||
otherModelValue: "=compareTo"
|
||||
},
|
||||
link: function(scope, element, attributes, ngModel) {
|
||||
|
||||
ngModel.$validators.compareTo = function(modelValue) {
|
||||
return modelValue == scope.otherModelValue;
|
||||
};
|
||||
|
||||
scope.$watch("otherModelValue", function() {
|
||||
ngModel.$validate();
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
angular.module('utils.directives', [])
|
||||
.directive("compareTo", compareTo);
|
||||
172
frontend/app/utils/services.js
Normal file
172
frontend/app/utils/services.js
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('utils.services', [])
|
||||
|
||||
.service('timerService', function($interval) {
|
||||
var timer;
|
||||
var listeners = {};
|
||||
|
||||
this.startTimer = function(ms) {
|
||||
timer = $interval(function() {
|
||||
_.each(listeners, function(listener) {
|
||||
listener();
|
||||
});
|
||||
}, ms);
|
||||
}
|
||||
|
||||
this.stopTimer = function(){
|
||||
$interval.cancel(timer);
|
||||
}
|
||||
|
||||
this.register = function(callback, key){
|
||||
// console.log("Registering requests for", key);
|
||||
return listeners[key] = callback;
|
||||
}
|
||||
|
||||
this.remove = function(key){
|
||||
// console.log("Destroying requests for", key);
|
||||
delete listeners[key];
|
||||
}
|
||||
})
|
||||
|
||||
.service('addressService', function(dataService, timerService, $localStorage, ngAudio) {
|
||||
var addrStats = {};
|
||||
var callback;
|
||||
var storage = $localStorage;
|
||||
|
||||
this.trackAddress = function (addr) {
|
||||
addrStats[addr] = {};
|
||||
track();
|
||||
}
|
||||
|
||||
this.deleteAddress = function (key) {
|
||||
delete addrStats[key];
|
||||
};
|
||||
|
||||
this.getData = function (){
|
||||
return addrStats;
|
||||
}
|
||||
|
||||
this.setAlarm = function(addr, bool){
|
||||
addrStats[addr].alarm = bool;
|
||||
storage.addrStats[addr].alarm = bool;
|
||||
}
|
||||
|
||||
var track = function(){
|
||||
_.each(addrStats, function(addr, key) {
|
||||
// Get Miner stats
|
||||
dataService.getData("/miner/"+key+"/stats", function(data){
|
||||
addrStats[key] = Object.assign(addr, data);
|
||||
|
||||
// check and inject alarm var
|
||||
if (addr.alarm == undefined) {
|
||||
addr.alarm = false;
|
||||
}
|
||||
|
||||
// Set default miner name address
|
||||
if (addr.name === undefined) {
|
||||
addr.name = key;
|
||||
}
|
||||
|
||||
// update
|
||||
storage.addrStats = addrStats;
|
||||
callback(addrStats);
|
||||
});
|
||||
|
||||
// Get miner worker ids
|
||||
dataService.getData("/miner/"+key+"/identifiers", function(minerIDs){
|
||||
addrStats[key].ids = minerIDs;
|
||||
});
|
||||
|
||||
dataService.getData("/miner/"+key+"/stats/allWorkers", function(workerStats){
|
||||
addrStats[key].workerStats = workerStats;
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
this.start = function (cb){
|
||||
timerService.register(track, 'minerStats');
|
||||
addrStats = storage.addrStats || {} ;
|
||||
callback = cb;
|
||||
track(); // also run immediately
|
||||
}
|
||||
})
|
||||
|
||||
.service('minerService', function($filter, dataService) {
|
||||
var minerStats = {};
|
||||
var callback;
|
||||
var status = true; // check pause
|
||||
|
||||
this.runService = function (bool) {
|
||||
status = bool;
|
||||
}
|
||||
|
||||
this.updateStats = function (addrs, callback) {
|
||||
|
||||
// initalise addrs
|
||||
if(!status) return 0;
|
||||
|
||||
_.each(addrs, function (data, addr) {
|
||||
|
||||
if (minerStats[addr] === undefined) minerStats[addr] = {
|
||||
dataset : {},
|
||||
options : {
|
||||
series: [],
|
||||
allSeries: [],
|
||||
axes: {
|
||||
x: {
|
||||
key: "ts",
|
||||
type: "date"
|
||||
}
|
||||
}
|
||||
},
|
||||
table_selected: [],
|
||||
table_options: {
|
||||
rowSelection: true,
|
||||
multiSelect: true
|
||||
}
|
||||
};
|
||||
|
||||
dataService.getData("/miner/"+addr+"/chart/hashrate/allWorkers", function(allWorkersData){
|
||||
// Convert all dates to object
|
||||
|
||||
_.each(allWorkersData, function (workerData, mid) {
|
||||
for(var i = 0 ; i < workerData.length; i++){
|
||||
allWorkersData[mid][i].ts = new Date(allWorkersData[mid][i].ts);
|
||||
}
|
||||
|
||||
minerStats[addr].dataset[mid] = workerData;
|
||||
|
||||
minerStats[addr].options.allSeries = _.unionBy(minerStats[addr].options.allSeries, [{
|
||||
axis: "y",
|
||||
id: mid,
|
||||
dataset: mid,
|
||||
label: mid,
|
||||
key: "hs",
|
||||
color: (minerStats[addr].options.series[mid]===undefined) ? randomColor() : minerStats[addr].options.series[mid].color,
|
||||
type: ['line', 'area'],
|
||||
//interpolation: { mode: "basis"},
|
||||
defined: function (value){
|
||||
//console.log(value);
|
||||
return (value !== undefined || value.x !== undefined || value.y !== undefined) ;
|
||||
}
|
||||
}], 'id');
|
||||
});
|
||||
|
||||
// only display selected miners
|
||||
var selected = minerStats[addr].selected;
|
||||
if(minerStats[addr].table_selected.length < 1) {
|
||||
selected = _.union(minerStats[addr].table_selected, ['global']);
|
||||
}
|
||||
|
||||
minerStats[addr].options.series = _.intersectionWith(minerStats[addr].options.allSeries, selected, function(ser, sel) { return ( ser.id == sel ) });
|
||||
});
|
||||
|
||||
// report back
|
||||
callback(minerStats);
|
||||
|
||||
});
|
||||
};
|
||||
});
|
||||
35
frontend/app/utils/strings.js
Normal file
35
frontend/app/utils/strings.js
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('utils.strings', [])
|
||||
|
||||
.filter('toXMR', function() {
|
||||
return function(amount) {
|
||||
return amount / 1000000000000;
|
||||
};
|
||||
})
|
||||
|
||||
.filter('toHashRate', function() {
|
||||
return function(hashes) {
|
||||
if (hashes > 1000000) {
|
||||
return Math.floor(hashes / 1000000) + "." + (hashes % 1000000).toString().substring(0, 1) + " MH/s"
|
||||
}
|
||||
if (hashes > 1000) {
|
||||
return Math.floor(hashes / 1000) + "." + (hashes % 1000).toString().substring(0, 1) + " KH/s"
|
||||
}
|
||||
return ( hashes || 0 ) + " H/s"
|
||||
};
|
||||
})
|
||||
|
||||
.filter('hashToLink', function($sce) {
|
||||
return function(hash, type) {
|
||||
var str = (hash == undefined) ? 'none' : "<a class=\"md-body-2\" target=\"_new\" href=\"https://xmrchain.net/"+type+"/" + hash + "\">" + hash + "</a>";
|
||||
return $sce.trustAsHtml(str);
|
||||
};
|
||||
})
|
||||
|
||||
.filter('difficultyToHashRate', function() {
|
||||
return function(hashrate) {
|
||||
return Math.floor(hashrate / 120)
|
||||
};
|
||||
});
|
||||
|
||||
5
frontend/app/welcome.html
Normal file
5
frontend/app/welcome.html
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<md-card-content>
|
||||
<div class="md-headline">Welcome to {{GLOBALS.pool_name}}</div>
|
||||
<h3>Update welcome content in app/welcome.html</h3>
|
||||
<p>Please stay tuned for more features and changes! If you have a feature request, please poke Snipa on irc.freenode.net in <a href="#/help/chat">#monero-pools</a></p>
|
||||
</md-card-content>
|
||||
23
frontend/bower.json
Normal file
23
frontend/bower.json
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"name": "xmrpoolui",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"angular": "~1.6.1",
|
||||
"angular-route": "~1.6.1",
|
||||
"angular-loader": "~1.6.1",
|
||||
"jquery": "~3.1.1",
|
||||
"angular-material": "~1.1.3",
|
||||
"angular-moment": "~1.0.1",
|
||||
"moment": "momentjs#^2.17.1",
|
||||
"ngstorage": "^0.3.11",
|
||||
"angular-audio": "^1.7.3",
|
||||
"angular-material-data-table": "^0.10.10",
|
||||
"angular-chart.js": "^1.1.1",
|
||||
"chart.js": "^2.4.0",
|
||||
"lodash": "^4.17.4",
|
||||
"angular_page_visibility": "angular-page-visibility#^0.0.4"
|
||||
},
|
||||
"resolutions": {
|
||||
"angular": "~1.6.1"
|
||||
}
|
||||
}
|
||||
86
frontend/gulpfile.js
Normal file
86
frontend/gulpfile.js
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
var gulp = require('gulp');
|
||||
var connect = require('gulp-connect');
|
||||
var manifest = require('gulp-manifest');
|
||||
|
||||
gulp.task('html', function(){
|
||||
return gulp.src(['app/**/*.html', '!app/vendor/**/*'])
|
||||
.pipe(connect.reload())
|
||||
.pipe(gulp.dest('build/'))
|
||||
});
|
||||
|
||||
gulp.task('css', function(){
|
||||
return gulp.src(['app/**/*.css', '!app/vendor/**/*'])
|
||||
.pipe(connect.reload())
|
||||
.pipe(gulp.dest('build/'))
|
||||
});
|
||||
|
||||
gulp.task('js', function(){
|
||||
return gulp.src(['app/**/*.js', '!app/vendor/**/*'])
|
||||
.pipe(connect.reload())
|
||||
.pipe(gulp.dest('build/'))
|
||||
});
|
||||
|
||||
gulp.task('assets', function(){
|
||||
return gulp.src('app/assets/*')
|
||||
.pipe(connect.reload())
|
||||
.pipe(gulp.dest('build/assets'))
|
||||
});
|
||||
|
||||
gulp.task('connect', function() {
|
||||
connect.server({
|
||||
root: 'build',
|
||||
livereload: true
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task('vendor', function() {
|
||||
return gulp.src([
|
||||
'app/vendor/**/dist/jquery.js',
|
||||
'app/vendor/**/angular.js',
|
||||
'app/vendor/**/angular-route.js',
|
||||
'app/vendor/**/angular-material.css',
|
||||
'app/vendor/**/angular-animate.js',
|
||||
'app/vendor/**/angular-aria.js',
|
||||
'app/vendor/**/angular-material.js',
|
||||
'app/vendor/**/angular-moment.js',
|
||||
'app/vendor/**/ngStorage.js',
|
||||
'app/vendor/**/app/angular.audio.js',
|
||||
'app/vendor/**/moment.js',
|
||||
'app/vendor/**/md-data-table.js',
|
||||
'app/vendor/**/md-data-table.css',
|
||||
'app/vendor/**/dist/chart.js',
|
||||
'app/vendor/**/dist/angular-chart.js',
|
||||
'node_modules/**/d3.js',
|
||||
'node_modules/**/LineChart.js',
|
||||
'node_modules/**/LineChart.css',
|
||||
'node_modules/**/randomColor.js',
|
||||
'app/vendor/**/lodash.js',
|
||||
'app/vendor/**/page_visibility.js'
|
||||
])
|
||||
.pipe(gulp.dest('build/vendor'))
|
||||
});
|
||||
|
||||
gulp.task('watch', function () {
|
||||
gulp.watch(['./app/**/*.html'], ['html', 'manifest']);
|
||||
gulp.watch(['./app/**/*.css'], ['css', 'manifest']);
|
||||
gulp.watch(['./app/**/*.js'], ['js', 'manifest']);
|
||||
gulp.watch(['./assets/*.*'], ['assets', 'manifest']);
|
||||
});
|
||||
|
||||
gulp.task('manifest', function(){
|
||||
gulp.src([
|
||||
'build/**/*'
|
||||
], { base: './build' })
|
||||
.pipe(manifest({
|
||||
hash: true,
|
||||
preferOnline: true,
|
||||
network: ['*'],
|
||||
filename: 'app.manifest',
|
||||
exclude: 'app.manifest'
|
||||
}))
|
||||
.pipe(connect.reload())
|
||||
.pipe(gulp.dest('build'));
|
||||
});
|
||||
|
||||
gulp.task('build', [ 'html', 'css', 'js', 'assets', 'vendor', 'manifest' ]);
|
||||
gulp.task('default', [ 'build', 'connect', 'watch' ]);
|
||||
23
frontend/package.json
Normal file
23
frontend/package.json
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"name": "xmrpoolui",
|
||||
"description": "UI for XMR Pool.net",
|
||||
"repository": "",
|
||||
"license": "?",
|
||||
"devDependencies": {
|
||||
"bower": "^1.7.7",
|
||||
"gulp": "^3.9.1",
|
||||
"gulp-connect": "^5.0.0",
|
||||
"gulp-manifest": "^0.1.1"
|
||||
},
|
||||
"scripts": {
|
||||
"postinstall": "bower install && gulp build",
|
||||
"update-deps": "npm update",
|
||||
"postupdate-deps": "bower update",
|
||||
"prestart": "npm install",
|
||||
"start": "gulp"
|
||||
},
|
||||
"dependencies": {
|
||||
"n3-charts": "^2.0.28",
|
||||
"randomcolor": "^0.4.4"
|
||||
}
|
||||
}
|
||||
42
frontend/readme.md
Normal file
42
frontend/readme.md
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
# Monero Pool frontend
|
||||
|
||||
### AngularJS based UI for [nodejs-pool](https://github.com/Snipa22/nodejs-pool)
|
||||
|
||||
### Features
|
||||
- See your hashrate on all pages
|
||||
- Track multiple payment addresses.
|
||||
- Hashrate siren when hashrate falls below a certain limit.
|
||||
- Per miner charts & Payment History.
|
||||
- Miner login and management for threhold and payment adjustment.
|
||||
- Admin UI for simple Pool management.
|
||||
- All the usual features + more.
|
||||
|
||||
### Run it
|
||||
|
||||
Home page html can be set in welcome.html
|
||||
Set pool params in app/globals.js.default and copy to app/globals.js
|
||||
|
||||
Requires NodeJS
|
||||
|
||||
```sh
|
||||
$ npm start # starts gulp + livereload, serves from ./build on 8080
|
||||
```
|
||||
|
||||
## Deploy
|
||||
```sh
|
||||
$ npm install # runs everything, serve from ./build
|
||||
```
|
||||
|
||||
### Todo
|
||||
|
||||
* Fix sort arrow styling
|
||||
* Network stats page.
|
||||
* Ship it deployment
|
||||
* Websockets
|
||||
* Miner graph colour picker
|
||||
|
||||
### Support
|
||||
* I'm usually on #monero-pools so drop me a line if you need help with something or have a feature request.
|
||||
|
||||
#### Coffee :P ?
|
||||
42yCGRP2p6bZzMjJxKpJtTFRz2x3X3eBYD97T17zdxC9NiGNWafCaU54MKWBZkHb9AVb4XBgcjkPGW8hjQyBM2vMMvVCzTj
|
||||
Loading…
Add table
Reference in a new issue