Repository: mesh0000/poolui Branch: master Commit: 4e8c550cb261 Files: 60 Total size: 113.3 KB Directory structure: gitextract_07_apddc/ ├── .bowerrc ├── .gitignore ├── LICENSE ├── app/ │ ├── admin/ │ │ ├── addport.js │ │ ├── adminlogin.html │ │ ├── adminlogin.js │ │ ├── config.html │ │ ├── config.js │ │ ├── dashboard.html │ │ ├── dashboard.js │ │ ├── editconfig.html │ │ ├── editconfig.js │ │ ├── editport.html │ │ ├── editport.js │ │ ├── ports.html │ │ ├── ports.js │ │ ├── workers.html │ │ └── workers.js │ ├── admin.html │ ├── admin.js │ ├── app.css │ ├── app.js │ ├── globals.default.js │ ├── index.html │ ├── user/ │ │ ├── blocks/ │ │ │ ├── blocks.html │ │ │ └── blocks.js │ │ ├── dashboard/ │ │ │ ├── dashboard.html │ │ │ ├── dashboard.js │ │ │ ├── minerpayments.html │ │ │ ├── minerpayments.js │ │ │ └── poolstats.html │ │ ├── help/ │ │ │ ├── chat.html │ │ │ ├── chat.js │ │ │ ├── faq.html │ │ │ ├── faq.js │ │ │ ├── getting_started.html │ │ │ ├── getting_started.js │ │ │ ├── portsmodal.html │ │ │ └── portsmodal.js │ │ ├── home/ │ │ │ ├── console.html │ │ │ ├── console.js │ │ │ ├── home.html │ │ │ ├── home.js │ │ │ ├── login.html │ │ │ └── login.js │ │ ├── network/ │ │ │ ├── network.html │ │ │ └── network.js │ │ ├── payments/ │ │ │ ├── payments.html │ │ │ └── payments.js │ │ └── ports/ │ │ ├── ports.html │ │ └── ports.js │ ├── utils/ │ │ ├── dataservice.js │ │ ├── directives.js │ │ ├── services.js │ │ └── strings.js │ └── welcome.html ├── bower.json ├── gulpfile.js ├── package.json └── readme.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: .bowerrc ================================================ { "directory": "app/vendor" } ================================================ FILE: .gitignore ================================================ logs/* */.log !.gitkeep node_modules/ vendor/ tmp .DS_Store .idea app/vendor/ build/ app/globals.js ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2017 Snipa22 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. ================================================ FILE: app/admin/addport.js ================================================ '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'); }); } }; }); ================================================ FILE: app/admin/adminlogin.html ================================================

Pool Admin Login

person lock
Remember me

{{status}}

Login
================================================ FILE: app/admin/adminlogin.js ================================================ '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'); }; }); ================================================ FILE: app/admin/config.html ================================================
Pool Config
{{selected.length}} {{selected.length > 1 ? 'items' : 'item'}} selected

ID

Item

Value

Type

Module

mode_edit {{config.id}} {{config.item}} {{config.value}} {{config.type}} {{config.module}}
================================================ FILE: app/admin/config.js ================================================ '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(); }); ================================================ FILE: app/admin/dashboard.html ================================================

{{type}}

Owed

{{stats.owed | toXMR | number }} XMR

Paid

{{stats.paid | toXMR | number }} XMR

Mined

{{stats.mined | toXMR | number}} XMR

Shares

{{stats.shares | number }}

Target Shares

{{stats.targetShares | number}}

Wallet

Height

{{ pool_wallet.height | number }}

Unlocked

{{ pool_wallet.unlocked | toXMR }} XMR

Balance

{{ pool_wallet.balance | toXMR }} XMR

Timestamp

{{ pool_wallet.ts | date }}

Height

Unlocked

Balanced

Timestamp

{{history.height | number}}

{{history.balance | toXMR }} XMR

{{history.unlocked | toXMR }} XMR

({{history.ts | date:'hh:mm:ss dd/MM/yy'}})
================================================ FILE: app/admin/dashboard.js ================================================ '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; }); }); ================================================ FILE: app/admin/editconfig.html ================================================

Edit Config

error_outline   All fields are required.

Save Cancel
================================================ FILE: app/admin/editconfig.js ================================================ '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'); }); } }; }); ================================================ FILE: app/admin/editport.html ================================================

Edit Port

error_outline   All fields are required.

PPLNS PPS SOLO Hidden SSL
Save Cancel
================================================ FILE: app/admin/editport.js ================================================ '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'); }); } }; }); ================================================ FILE: app/admin/ports.html ================================================
Ports add Add Port
{{selected.length}} {{selected.length > 1 ? 'items' : 'item'}} selected

Port

Difficulty

Description

Type

Hidden

SSL

mode_edit {{port.port}} {{port.diff}} {{port.desc}} {{port.portType}} done clear done clear delete
================================================ FILE: app/admin/ports.js ================================================ '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(); }); ================================================ FILE: app/admin/workers.html ================================================

Hashrate

Address

Paid

Due

Total #s

Good #s

Bad #s

Workers

Last Hash

{{worker.hashRate | toHashRate}} {{worker.address}} {{worker.paid | toXMR}} XMR {{worker.due | toXMR }} XMR {{worker.totalHashes | number }} {{worker.goodShares | number }} {{worker.badShares | number }} {{worker.workers.length}}

{{worker.lastHash | date:'hh:mm:ss dd/MM/yy'}}
================================================ FILE: app/admin/workers.js ================================================ 'use strict'; app.controller('AdminWorkersCtrl', function($scope, $location, $route, dataService) { $scope.selected = []; $scope.promise = dataService.getData("/admin/userList", function(data) { $scope.pool_workers = data; }); }); ================================================ FILE: app/admin.html ================================================ XMRPool.net - Mine XMR/Monero or BTC/Bitcoin

Pool Admin CP

Logout
Dashboard Workers Ports Config
================================================ FILE: app/admin.js ================================================ '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'); } }); ================================================ FILE: app/app.css ================================================ /*** 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; } ================================================ FILE: app/app.js ================================================ '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 }); ================================================ FILE: app/globals.default.js ================================================ '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 }; }); ================================================ FILE: app/index.html ================================================ XMRPool.net - Mine XMR/Monero or BTC/Bitcoin
home Home dashboard Dashboard reorder Blocks payments Payment flight_land Ports language Network group Support launch Getting Started help_outline FAQ
Powered by nodejs-pool & poolui
MIT Licence
================================================ FILE: app/user/blocks/blocks.html ================================================
Blocks Found

Valid

Time Found

Height

Difficulty

Hash

Shares

Luck

Maturity

Pool

{{::block.icon}}

{{::block.ts | date:"hh:mm:ss dd/MM/yy"}}

{{::block.height | number}}

{{::block.diff | number}}

{{::block.shares | number}}

{{::block.luck | number:2}} %

lock_open {{-block.maturity}} blocks ago

{{block.maturity}} to go

{{::block.pool_type}}

================================================ FILE: app/user/blocks/blocks.js ================================================ '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); }); }); ================================================ FILE: app/user/dashboard/dashboard.html ================================================
Network Stats

Hash Rate

{{network.difficulty | difficultyToHashRate | toHashRate}}

Difficulty

{{network.difficulty | number }}

Hash

Height

{{network.height | number }}

Reward

{{network.value | toXMR}}

Time Found

Pool Stats

Hash Rate

{{ poolStats[pooltype].pool_statistics.hashRate | toHashRate }}

Height

{{poolStats[pooltype].pool_statistics.lastBlockFound | number}}

Last Block

Block Reward

{{lastBlock[pooltype].value | toXMR | number:10}} XMR

Time Found

Never {{poolStats[pooltype].pool_statistics.lastBlockFoundTime*1000 | date:'hh:mm:ss dd/MM/yy'}}

Fees

{{ poolStats[pooltype].pool_statistics.fee }} %

Blocks Found

{{ poolStats[pooltype].pool_statistics.totalBlocksFound || '0' }}

Miners

{{ poolStats[pooltype].pool_statistics.miners || '0' }}

Miners Paid

{{ poolStats[pooltype].pool_statistics.totalMinersPaid || '0' }}

Payments sent

{{ poolStats[pooltype].pool_statistics.totalPayments || '0' }}

account_balance_wallet
add Track Live Stats
account_balance {{addr}} ( Last Hash : Never ) {{miner.lastHash*1000 | date:'hh:mm:ss dd/MM/yy'}} clear

Hash Rate

{{miner.hash | toHashRate}}

Total Hashes

{{miner.totalHashes | number}}

Total Due

{{miner.amtDue | toXMR | number:10}} XMR

Total Paid

{{miner.amtPaid | toXMR | number:10}} XMR

Worker

#'s

Total #'s

Last Hash

{{id}}

{{minerStats[addr].dataset[id][0].hs | toHashRate}}

{{addrStats[addr].workerStats[id].totalHash | number}}

{{minerStats[addr].dataset[id][0].ts | date: 'hh:mm:ss dd/MM/yy'}}

Valid Shares

{{miner.validShares | number}} check

Invalid Shares

{{miner.invalidShares | number}} clear

alarm
View Payments
================================================ FILE: app/user/dashboard/dashboard.js ================================================ '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); }); }); ================================================ FILE: app/user/dashboard/minerpayments.html ================================================

Payment History

clear

To : {{addr}}

Time

Type

Amount

Txn Hash

Mixins

{{payment.ts*1000 | date:'hh:mm:ss dd/MM/yy'}}

{{payment.pt}}

{{payment.amount | toXMR }} XMR

{{payment.mixin}}

Close
================================================ FILE: app/user/dashboard/minerpayments.js ================================================ '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') } }); ================================================ FILE: app/user/dashboard/poolstats.html ================================================
multiline_chart
{{poolStats.global.totalHashes | number}} Hashes Accepted
widgets
{{poolStats.global.totalBlocksFound}}
Blocks Found () {{poolStats.global.lastBlockFoundTime*1000 | date:'hh:mm:ss dd/MM/yy'}}
memory
{{poolStats.global.miners}} Miners Connected
attach_money
{{poolStats.global.totalMinersPaid}} Miners Paid ({{poolStats.global.totalPayments}} Payments)
================================================ FILE: app/user/help/chat.html ================================================
Support on #monero-pools (Ask for help or say hello!)
================================================ FILE: app/user/help/chat.js ================================================ 'use strict'; app.controller('ChatCtrl', function() { }); ================================================ FILE: app/user/help/faq.html ================================================
Pool FAQ

{{topic}}

{{question.title}}

{{question.media.title}}


Contents

{{topic}}

{{q.title}}

================================================ FILE: app/user/help/faq.js ================================================ '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.
"), 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.

Read
Getting Started 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.
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.

An accurate estimate of earnings of the pool can be calculated by observing average daily number of blocks found ... followed by some mathematics?

* 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.

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.

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.

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.

To change your payment threshold, click the wrench after you login via \"Login\" button on the top right.

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.

To configure BTC payments please have a look at Getting Started 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.

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.

This assures you not creating too many or too few requests to your server optimizing bandwidth consumption and server loads.

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 https://github.com/Snipa22/nodejs-pool/issues/27.") } ], "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.

http://monerobechmarks.byethost5.com/ 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 -- Getting Started.") } ], "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.

http://monerobechmarks.byethost5.com/ 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("http://reddit.com/r/moneromining/
http://monero.stackexchange.com/") } ] } // end }); ================================================ FILE: app/user/help/getting_started.html ================================================
Getting Started with {{GLOBALS.pool_name}}

If you are new to mining, this is the place for you.

take a deep breath, its only confusing at first but pure joy once up and running.

1. Create a Wallet

Both online wallets and hardware wallets have their pros and cons.

MyMonero : MyMonero is an online wallet maintained by the core monero team, so while safe is still suceptable to hacks.

Monero GUI Wallet : The safest way to safely store and secure your XMR, although it would be your responsibility to kee your XMR Safe.

2. Download mining software

AMD Miner

Nvidia Miner

CPU Miner (AESNI CPU's)

CPU Miner (Older CPU's})

3. Pick a server and port

There are 2 important things here the URL and the port that you are going to connect to.

You need to pick your URL and Port from the Ports List. Here you'll notice theres Global, PPLNS, Solo (?)

Global is simply a collection of both PPLNS & Solo ports powerd by GEODNS.
GEODNS automagically finds the closest server to you and could also be used to shares you in case of a regional outage.

If you prefer to shave off a few of those ms and connect directly, you have the option to select your preferred endpoint too.

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).

If you are an advanced user or would like to set a fixed difficulty. examples below.

View Ports list
The moment

OK So by now you should have a wallet, mining software, url and port. Its time to put them together.

Here are some examples of the parameters that you need to pass on to your miner software, the format matters so be careful.

Depending on your miner you need to add the required parameters via command line or config.txt

Required fields are payment address and MinerIdentifier

Username format : address.paymentID+FixedDifficulty
Password Format : MinerIdentifier:Email
Username / Password Samples

## {{ sample.desc }}

{{ sample.type }} : {{ sample.sample }}

(e.g. miner.exe -u {{(sample.type=="Username") ? sample.sample : 'paymentAddress'}} -p {{(sample.type=="Password") ? sample.sample : 'worker'}})

Happy Mining !

================================================ FILE: app/user/help/getting_started.js ================================================ '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 } ] }); ================================================ FILE: app/user/help/portsmodal.html ================================================

Ports List

clear

Server

Port

Port Type

Difficulty

Miners

Current Block ID

Block Time

Description

{{portinfo.host.hostname}}

{{portinfo.port}}

{{portinfo.pool_type}}

{{portinfo.difficulty}}

{{portinfo.miners}}

{{portinfo.host.blockID}}

{{portinfo.host.blockIDTime * 1000 | date: 'hh:mm:ss - dd/MM/yy'}}

{{portinfo.description}}

Close
================================================ FILE: app/user/help/portsmodal.js ================================================ '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') } }); ================================================ FILE: app/user/home/console.html ================================================

Miner Console

clear

You currently have no payment threshold set, defaults are in use.

This value cannot be lower than {{min_wallet_payout | toXMR}} XMR

The pool will pay out to your wallet once your total due exceeds the payment threshold

Receive miner hashrate alerts
Please confirm your new password
Logout

{{status}}

Update {{currentTab}}
================================================ FILE: app/user/home/console.js ================================================ '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(); } }); ================================================ FILE: app/user/home/home.html ================================================
Pool Config

PPLNS Fee

{{config.pplns_fee}}% 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 }}%

Solo Fee

{{config.solo_fee}}% 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}}%

BTC Fee

{{config.btc_fee}}%

Minimum payout (Wallet)

{{config.min_wallet_payout | toXMR}} XMR

Minimum payout (BTC)

{{config.min_btc_payout | toXMR}} XMR

Minimum payout (Exchange)

{{config.min_exchange_payout | toXMR}} XMR

Block Maturity Depth

{{config.maturity_depth}}

Minimum Denomination

{{config.min_denom | toXMR}}

================================================ FILE: app/user/home/home.js ================================================ 'use strict'; app.controller('HomeCtrl', function($scope, $route, dataService, timerService) { }); ================================================ FILE: app/user/home/login.html ================================================ ================================================ FILE: app/user/home/login.js ================================================ '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(); } }); ================================================ FILE: app/user/network/network.html ================================================

Coming SoOn..

================================================ FILE: app/user/network/network.js ================================================ '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); // }); }); ================================================ FILE: app/user/payments/payments.html ================================================
Payments Made

Time Sent

Transaction Hash

Amount

Fee

Mixin

Payees

{{payment.ts | date:'hh:mm:ss dd/MM/yy'}}

{{payment.value | toXMR}} XMR

{{payment.fee | toXMR }} XMR

{{payment.mixins}}

{{payment.payees}}

================================================ FILE: app/user/payments/payments.js ================================================ '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(); }); ================================================ FILE: app/user/ports/ports.html ================================================
Ports List

Server

Port

Port Type

Difficulty

Miners

Current Block ID

Block Time

Description

{{portinfo.host.hostname}}

{{portinfo.port}}

{{portinfo.pool_type}}

{{portinfo.difficulty}}

{{portinfo.miners}}

{{portinfo.host.blockID}}

{{portinfo.host.blockIDTime * 1000 | date: 'hh:mm:ss - dd/MM/yy'}}

{{portinfo.description}}

================================================ FILE: app/user/ports/ports.js ================================================ '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; }); }); ================================================ FILE: app/utils/dataservice.js ================================================ 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 } }) ================================================ FILE: app/utils/directives.js ================================================ 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); ================================================ FILE: app/utils/services.js ================================================ '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); }); }; }); ================================================ FILE: app/utils/strings.js ================================================ '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' : "" + hash + ""; return $sce.trustAsHtml(str); }; }) .filter('difficultyToHashRate', function() { return function(hashrate) { return Math.floor(hashrate / 120) }; }); ================================================ FILE: app/welcome.html ================================================
Welcome to {{GLOBALS.pool_name}}

Update welcome content in app/welcome.html

Please stay tuned for more features and changes! If you have a feature request, please poke Snipa on irc.freenode.net in #monero-pools

================================================ FILE: bower.json ================================================ { "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" } } ================================================ FILE: gulpfile.js ================================================ 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' ]); ================================================ FILE: package.json ================================================ { "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" } } ================================================ FILE: readme.md ================================================ # 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