├── logs └── .logs ├── VERSION ├── config ├── sitemap.conf ├── sia-cluster.local.conf-example ├── sia-cluster.conf ├── settings.json └── i18n.conf ├── lib ├── manage │ ├── index.js │ ├── resources │ │ ├── login.css │ │ └── login.js │ ├── manage.ejs │ ├── login.ejs │ └── lib │ │ └── manage.js ├── markets.js ├── exchanges │ ├── exchange.js │ ├── bfx.js │ ├── yunbi.js │ ├── btc-e.js │ ├── kraken.js │ ├── polo.js │ └── ecb.js ├── api-base.js ├── helper.js ├── api.js └── price-aggregator.js ├── http ├── favicon.ico ├── fonts │ ├── ETmodules_v2.eot │ ├── ETmodules_v2.ttf │ ├── ETmodules_v2.woff │ ├── MaterialIcons-Regular.eot │ ├── MaterialIcons-Regular.ttf │ ├── MaterialIcons-Regular.woff │ ├── MaterialIcons-Regular.woff2 │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ └── glyphicons-halflings-regular.woff2 ├── images │ ├── sia-cluster-16px.png │ ├── sia-cluster-256px.png │ ├── sia-cluster-32px.png │ ├── sia-cluster-512px.png │ ├── placeholder.svg │ └── sia-cluster.svg ├── site │ └── scripts │ │ ├── index.js │ │ └── jquery.easing.1.3.js ├── scripts │ ├── scrypt.js │ └── chart.js ├── app │ ├── panel-style.html │ ├── deps.html │ ├── css │ │ └── style.css │ ├── sia-colors.html │ ├── node-setting-style.html │ ├── sia-network-panel.html │ ├── sia-variable-graph.html │ ├── app-style.html │ ├── app.js │ ├── sia-pricing.html │ ├── sia-cluster-storage.html │ ├── sia-network-peers-panel.html │ ├── themes │ │ └── sia-cluster.html │ ├── sia-cluster.html │ └── sia.html └── api.js ├── .gitignore ├── resources └── images │ └── sia-cluster-screenshot-01.png ├── deploy ├── systemd │ ├── siad.service │ └── sia-cluster.service └── upstart │ └── sia-cluster.conf ├── tools └── release │ ├── NOTES.md │ ├── bundle │ ├── pubkey │ ├── build │ └── setup.js ├── views ├── site │ ├── index.ejs │ ├── partial │ │ ├── footer.ejs │ │ └── header.ejs │ └── login.ejs ├── error.ejs └── app │ └── app.ejs ├── certificates ├── sia-cluster.crt └── sia-cluster.key ├── LICENSE ├── package.json ├── run.js └── sia-cluster.js /logs/.logs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | v0-9-2-rc1 -------------------------------------------------------------------------------- /config/sitemap.conf: -------------------------------------------------------------------------------- 1 | { 2 | baseUrl : "http://127.0.0.1" 3 | } 4 | -------------------------------------------------------------------------------- /lib/manage/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/manage'); 2 | -------------------------------------------------------------------------------- /http/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aspectron/sia-cluster/HEAD/http/favicon.ico -------------------------------------------------------------------------------- /http/fonts/ETmodules_v2.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aspectron/sia-cluster/HEAD/http/fonts/ETmodules_v2.eot -------------------------------------------------------------------------------- /http/fonts/ETmodules_v2.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aspectron/sia-cluster/HEAD/http/fonts/ETmodules_v2.ttf -------------------------------------------------------------------------------- /http/fonts/ETmodules_v2.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aspectron/sia-cluster/HEAD/http/fonts/ETmodules_v2.woff -------------------------------------------------------------------------------- /http/images/sia-cluster-16px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aspectron/sia-cluster/HEAD/http/images/sia-cluster-16px.png -------------------------------------------------------------------------------- /http/images/sia-cluster-256px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aspectron/sia-cluster/HEAD/http/images/sia-cluster-256px.png -------------------------------------------------------------------------------- /http/images/sia-cluster-32px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aspectron/sia-cluster/HEAD/http/images/sia-cluster-32px.png -------------------------------------------------------------------------------- /http/images/sia-cluster-512px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aspectron/sia-cluster/HEAD/http/images/sia-cluster-512px.png -------------------------------------------------------------------------------- /http/fonts/MaterialIcons-Regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aspectron/sia-cluster/HEAD/http/fonts/MaterialIcons-Regular.eot -------------------------------------------------------------------------------- /http/fonts/MaterialIcons-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aspectron/sia-cluster/HEAD/http/fonts/MaterialIcons-Regular.ttf -------------------------------------------------------------------------------- /http/fonts/MaterialIcons-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aspectron/sia-cluster/HEAD/http/fonts/MaterialIcons-Regular.woff -------------------------------------------------------------------------------- /http/fonts/MaterialIcons-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aspectron/sia-cluster/HEAD/http/fonts/MaterialIcons-Regular.woff2 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | bower_components/ 3 | config/*.local.conf 4 | config/*.local.json 5 | temp/ 6 | uuid 7 | logs/* 8 | -------------------------------------------------------------------------------- /http/site/scripts/index.js: -------------------------------------------------------------------------------- 1 | (function($){ 2 | 3 | $(document).ready(function(){ 4 | Api.hideLoading(); 5 | }) 6 | 7 | })(jQuery); 8 | -------------------------------------------------------------------------------- /http/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aspectron/sia-cluster/HEAD/http/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /http/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aspectron/sia-cluster/HEAD/http/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /http/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aspectron/sia-cluster/HEAD/http/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /http/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aspectron/sia-cluster/HEAD/http/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /resources/images/sia-cluster-screenshot-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aspectron/sia-cluster/HEAD/resources/images/sia-cluster-screenshot-01.png -------------------------------------------------------------------------------- /deploy/systemd/siad.service: -------------------------------------------------------------------------------- 1 | # /usr/lib/systemd/siad.service 2 | 3 | [Unit] 4 | Description=Sia Daemon 5 | 6 | [Service] 7 | Type=simple 8 | PIDFile=/var/run/siad.pid 9 | WorkingDirectory=/home/userfolder/sia 10 | ExecStart=/home/userfolder/sia/siad 11 | 12 | [Install] 13 | WantedBy=multi-user.target 14 | -------------------------------------------------------------------------------- /deploy/systemd/sia-cluster.service: -------------------------------------------------------------------------------- 1 | # /usr/lib/systemd/sia-cluster.service 2 | 3 | [Unit] 4 | Description=Sia Cluster 5 | 6 | [Service] 7 | Type=simple 8 | PIDFile=/var/run/sia-cluster.pid 9 | WorkingDirectory=/home/userfolder/sia-cluster 10 | ExecStart=/home/userfolder/node/bin/node run sia-cluster 11 | 12 | [Install] 13 | WantedBy=multi-user.target 14 | -------------------------------------------------------------------------------- /deploy/upstart/sia-cluster.conf: -------------------------------------------------------------------------------- 1 | # this should live in /etc/init 2 | description "sia-cluster" 3 | 4 | # start process on system startup 5 | start on filesystem 6 | stop on shutdown 7 | 8 | # Automatically Respawn: 9 | respawn 10 | respawn limit 20 5 11 | 12 | script 13 | cd /home/userfolder/releases/sia-cluster 14 | exec ../node/bin/node run sia-cluster 15 | end script 16 | -------------------------------------------------------------------------------- /tools/release/NOTES.md: -------------------------------------------------------------------------------- 1 | 2 | ### Dependencies for build process: 3 | 4 | All Platforms: 5 | 6 | * `npm -g install bower` 7 | * `npm -g install flatten-packages` 8 | 9 | win64: 10 | 11 | * NodeJs 12 | * Visual Studio Community Edition 13 | * Python 2.7 (not 3.x!) 14 | * Git for Windows 15 | * GNU zip for Windows (mingw64) 16 | 17 | linux64: 18 | 19 | * `sudo apt-get install git` 20 | * NodeJs 21 | * Pyton 22 | 23 | 24 | -------------------------------------------------------------------------------- /lib/manage/resources/login.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | font-family: "Exo"; 3 | font-size: 18px; 4 | margin: 0px; 5 | overflow: hidden; 6 | } 7 | 8 | input#user, input#pass { 9 | font-family: "Exo"; 10 | font-size: 18px; 11 | padding: 4px; 12 | } 13 | 14 | #status { 15 | margin-top: 16px; 16 | text-align: left; 17 | color: #900; 18 | } 19 | #login { 20 | border: 1px solid #888; 21 | background: #eee; 22 | padding: 5px 16px; 23 | } -------------------------------------------------------------------------------- /http/scripts/scrypt.js: -------------------------------------------------------------------------------- 1 | var scrypt = scrypt_module_factory(); 2 | 3 | scrypt.hex2uint8array = function(hex) { 4 | var bytes = new Uint8Array(hex.length/2); 5 | for(var i=0; i< hex.length-1; i+=2){ 6 | bytes[i] = parseInt(hex.substr(i, 2), 16); 7 | } 8 | return bytes; 9 | } 10 | 11 | scrypt.createHash = function(data, config) { 12 | var hash = scrypt.crypto_scrypt(scrypt.encode_utf8(data), scrypt.hex2uint8array(config.salt), config.n, config.r, config.p, config.keyLength); 13 | return scrypt.to_hex(hash); 14 | } 15 | 16 | -------------------------------------------------------------------------------- /views/site/index.ejs: -------------------------------------------------------------------------------- 1 | <% req.__scriptAtHeader = false; %> 2 | <% req.__script = "underscore-min;index" %> 3 | <% include partial/header %> 4 | 5 |
6 | 7 |
8 | 9 | 16 | 17 | 23 | <% include partial/footer %> 24 | -------------------------------------------------------------------------------- /certificates/sia-cluster.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICATCCAWoCCQDt1dDerT7q6DANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJB 3 | VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0 4 | cyBQdHkgTHRkMB4XDTE0MDUwNjA3MjAzNloXDTMwMDMxNjIxNTUxNlowRTELMAkG 5 | A1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0 6 | IFdpZGdpdHMgUHR5IEx0ZDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAymx7 7 | i02LQS3QBIxXvw5KzpvRcJjJco6gyTtuDRyLeGcSBWPKl0DJTEykIskD3QyoLTkL 8 | LtOMPdTLWCbSW7cyl6Uq/3qznnxjWB2A3xPV1Nvv1X0hBd0uovMAmwLtnDYgfd2Q 9 | SEJwLberGq+0OtodVZTH3ul8sBZ0cOwtKx6wlKcCAwEAATANBgkqhkiG9w0BAQUF 10 | AAOBgQBWSVHgxQPHGaQdVenAZlKdt+MR69Z224Ou+nmoXF49W1FThxc4oRG0t4xi 11 | NCglhytCiK/PcBvyt+3PyKYILD7PxUAsdmALq9nD+WG6rq7MIejfws7a6v5P7P/M 12 | fgQni0BRTefL5t3ZlxKGCgBR93gC/J8xoPe6DZzGytn+EMwW+A== 13 | -----END CERTIFICATE----- 14 | -------------------------------------------------------------------------------- /http/app/panel-style.html: -------------------------------------------------------------------------------- 1 | 2 | 22 | -------------------------------------------------------------------------------- /certificates/sia-cluster.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXAIBAAKBgQDKbHuLTYtBLdAEjFe/DkrOm9FwmMlyjqDJO24NHIt4ZxIFY8qX 3 | QMlMTKQiyQPdDKgtOQsu04w91MtYJtJbtzKXpSr/erOefGNYHYDfE9XU2+/VfSEF 4 | 3S6i8wCbAu2cNiB93ZBIQnAtt6sar7Q62h1VlMfe6XywFnRw7C0rHrCUpwIDAQAB 5 | AoGALFT/5a1Q7zBqW2SlHvmxVnh3sRI1JDqqagfy/TogLXldUALf7qpIq8YpOFkP 6 | 2IyaFHVmxpWcJDqDYkX2UhHYKUwedQ/i+KdZ5M52mOP7iUHWC+5PYCYndJijVkdn 7 | 1f1kaUDVGCEkrdXqgcB/7voj6rbqdcStVLc9XGAdrdEligkCQQDy4EXM3An9Z1pk 8 | BER5elaiRxQtjHUlL2h2XewzPnNIYLh4CLEY0m6XWj7Il1HlWdbkK4lFpBjGrYQv 9 | 0jGLEXc1AkEA1VyhIW/D07afxqkLfpRCwN5SHWuIykSh/MnMdZMh6kORSxcrv/6v 10 | glYb061kDw5IploUVwrhrXNH4xOK/yFr6wJAWJ+xmKEqHAdcmmZcPh+AAVMCb+Ry 11 | 0pDMA3UePUyqcFyqs1IonTAcHqpVgoiE37W6jiO8wWaxi73BIFoIrgA/iQJBAIi6 12 | eY/B3c54w989SV5uiHCsiBbOaLSmUuB6OYpHJX7Imf1y9dhtz+9IW0DFZs+3KZth 13 | MpOtJ35N2A2O4o4ozs0CQGGPbkjxROSFuTzXmh8nWnFBi19W6IHjX6MY84TYRvol 14 | h5eXvetVF6dwIjAER5Zm3I036XUarbIoWnEzuUlOnWE= 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /lib/markets.js: -------------------------------------------------------------------------------- 1 | var POLO = require("./exchanges/polo"); 2 | var YUNBI = require("./exchanges/yunbi"); 3 | var BFX = require("./exchanges/bfx"); 4 | var BTCE = require("./exchanges/btc-e"); 5 | var KRAKEN = require("./exchanges/kraken"); 6 | var ECB = require("./exchanges/ecb"); 7 | var _ = require("iris-underscore"); 8 | 9 | 10 | function Markets(core, config) { 11 | var self = this; 12 | self.exchanges = { 13 | polo : new POLO(core, config), 14 | yunbi : new YUNBI(core, config), 15 | btce : new BTCE(core, config), 16 | // bfx : new BFX(core, config), 17 | kraken : new KRAKEN(core, config), 18 | ecb : new ECB(core, config) 19 | } 20 | 21 | self.update = function(callback) { 22 | 23 | var list = _.keys(self.exchanges); 24 | 25 | _.asyncMap(list, function(ident, callback) { 26 | 27 | self.exchanges[ident].syncTickers(function(err) { 28 | if(err) 29 | console.log(err); 30 | callback(); 31 | }); 32 | 33 | }, function() { 34 | callback(); 35 | }) 36 | 37 | 38 | } 39 | } 40 | 41 | module.exports = Markets; -------------------------------------------------------------------------------- /views/error.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= _T("SIA Cluster") %> 5 | 6 | 7 | 8 | 15 | 16 | 17 |
18 |
19 | 20 |
21 | <%= message %> 22 |
23 |
24 |
25 | <%= _T("Back to Home Page") %> 26 |
27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /http/images/placeholder.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | 10 | 12 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 ASPECTRON Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /lib/manage/manage.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%=_T("Accredited Access")%> || <%=_T("CMS")%> 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /config/sia-cluster.local.conf-example: -------------------------------------------------------------------------------- 1 | // 2 | // Sia Cluster local config file 3 | // 4 | // WARNING: You must create unique auth string that you use throughout your deployment! 5 | // 6 | // To create user account - https://github.com/aspectron/sia-cluster#user-access 7 | // 8 | 9 | { 10 | rpc : { 11 | // server 12 | nodes : { 13 | port : 58481, 14 | auth : "1299ece0263565a53df103a34910884d5016a10d86c06e5f309f17761a965d28" // your auth 15 | }, 16 | // client to remote server 17 | // remote_node_as_server_1 : { 18 | // address : ':17412', 19 | // auth : "1299ece0263565a53df103a34910884d5016a10d86c06e5f309f17761a965d28" // your auth 20 | // } 21 | }, 22 | 23 | http : { 24 | 25 | // 'localhost' to listen on local interface only, 26 | // useful if you are proxying via NGINX 27 | // 28 | // host : "127.0.0.1", 29 | 30 | 31 | // basicAuth : { 32 | // user : "sia", 33 | // pass : "q1w2e3r4" 34 | // } 35 | }, 36 | 37 | users : { 38 | "test": {pass: "13a5c202e320d0bf9bb2c6e2c7cf380a6f7de5d392509fee260b809c893ff2f9"} 39 | } 40 | } -------------------------------------------------------------------------------- /lib/manage/resources/login.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | $("#login").click(function() { 3 | var user = $("#user").val(); 4 | var pass = $("#pass").val(); 5 | if(!user || !pass) 6 | return $("#status").html("Please supply a valid username and password"); 7 | var hash = CryptoJS.SHA256(CryptoJS.enc.Utf8.parse(pass)).toString(); 8 | var sig = CryptoJS.HmacSHA256(CryptoJS.enc.Hex.parse(hash), CryptoJS.enc.Hex.parse(salt)).toString(); 9 | 10 | $.ajax({ 11 | url: '/manage/login', 12 | type: 'POST', 13 | dataType: "json", 14 | data: { 15 | user : user, 16 | sig : sig 17 | } 18 | }).done(function (data) { 19 | console.log("SUCCESS:",arguments); 20 | if(data.ack == salt) 21 | window.location.assign('/manage'); 22 | else 23 | $("#status").html(data.ack); 24 | }).fail(function (jqXHR) { 25 | console.log("FAILURE:",arguments); 26 | $("#status").html(jqXHR.statusText); 27 | }); 28 | }) 29 | $("#user").keydown(function(e) { 30 | if(e.which == 13) 31 | $("#login").trigger('click'); 32 | }) 33 | $("#pass").keydown(function(e) { 34 | if(e.which == 13) 35 | $("#login").trigger('click'); 36 | }) 37 | $("#user").focus(); 38 | }) -------------------------------------------------------------------------------- /lib/exchanges/exchange.js: -------------------------------------------------------------------------------- 1 | module.exports = Exchange; 2 | 3 | var util = require("util"); 4 | var events = require('events'); 5 | var request = require("request"); 6 | var _ = require("underscore"); 7 | 8 | function Exchange(core, config) { 9 | var self = this; 10 | events.EventEmitter.apply(this); 11 | self.verbose = config.verbose || false; 12 | self.ident = 'N/A'; 13 | self.ctx = { ts : 0, tickers : { } } 14 | 15 | self.fetch = function(url, callback) { 16 | core.verbose > 1 && console.log("-->",url); 17 | request({ 18 | url : url, 19 | timeout : 15 * 1000 20 | } , function(error, response, body) { 21 | if (!error && response.statusCode == 200) { 22 | var data; 23 | try { 24 | data = JSON.parse(body); 25 | } 26 | catch(err) { 27 | return callback(err); 28 | } 29 | 30 | return callback(null, data); 31 | } 32 | else 33 | if(!error && response.statusCode != 200) { 34 | console.log('-->', url); 35 | console.log("Invalid response code: "+response.statusCode); 36 | return callback(new Error("Invalid response code: "+response.statusCode)); 37 | } 38 | else 39 | { 40 | console.log('-->', url); 41 | console.log(error.toString().red.bold); 42 | return callback(error) 43 | } 44 | }); 45 | } 46 | 47 | 48 | 49 | } 50 | 51 | util.inherits(Exchange, events.EventEmitter); 52 | -------------------------------------------------------------------------------- /lib/exchanges/bfx.js: -------------------------------------------------------------------------------- 1 | module.exports = BFX; 2 | 3 | var util = require("util"); 4 | var events = require('events'); 5 | var request = require("request"); 6 | var Exchange = require("./exchange"); 7 | var _ = require("underscore"); 8 | 9 | var REQUEST_INTERVAL = 3000; 10 | 11 | function BFX(core) { 12 | var self = this; 13 | Exchange.apply(this,arguments); 14 | 15 | // --- 16 | 17 | var baseURL = "https://api.bitfinex.com/v1" 18 | 19 | function fetch(op, callback) { 20 | 21 | self.fetch(baseURL+op, callback); 22 | } 23 | 24 | self.syncTickers = function(callback) { 25 | 26 | var pairs = ["BTC/USD"]; 27 | 28 | var out = { } 29 | 30 | _.asyncMap(pairs, function(pair, callback) { 31 | 32 | var ident = pair.replace('/',''); 33 | 34 | fetch('/pubticker/'+ident, function(err, o) { 35 | if(err) 36 | return callback(err); 37 | 38 | out[pair] = { 39 | ask : parseFloat(o.ask), 40 | bid : parseFloat(o.bid), 41 | volume : parseFloat(o.volume), 42 | high : parseFloat(o.high), 43 | low : parseFloat(o.low) 44 | } 45 | 46 | dpc(self.ctx.ts ? REQUEST_INTERVAL : 100, callback); 47 | 48 | }) 49 | 50 | }, function() { 51 | 52 | self.ctx.tickers = out; 53 | self.ctx.ts = Date.now(); 54 | callback(); 55 | }) 56 | 57 | 58 | } 59 | 60 | } 61 | 62 | util.inherits(BFX, events.EventEmitter); 63 | -------------------------------------------------------------------------------- /lib/api-base.js: -------------------------------------------------------------------------------- 1 | var _ = require("underscore"); 2 | var request = require("request"); 3 | 4 | function APIBase(core, apiName){ 5 | var self = this; 6 | 7 | self.init = function(){} 8 | 9 | self.initHttp = function(app){} 10 | 11 | self.initRPC = function(rpc){} 12 | 13 | self.rpcRequest = function rpcRequest(socket, data, callback){ 14 | self.getSocketUser(socket, function(err, user){ 15 | if (err) 16 | return callback(err); 17 | 18 | if(!user || !user.token) 19 | return callback({code: "USER-TOKEN-MISSING", error: "User token missing in session"}); 20 | 21 | data.token = user.token; 22 | data.op = data.op.replace("ws::", ""); 23 | core.rpc.dispatch(data, function(err, result){ 24 | console.log(('RPC-RESULT:'+data.op).redBG.white, err, result); 25 | 26 | if (err && err.logout) 27 | delete user.token; 28 | 29 | callback(err, result); 30 | }); 31 | }); 32 | } 33 | 34 | self.getSocketUser = function getSocketUser(socket, callback){ 35 | core.getSocketSession(socket, function(err, session){ 36 | if (err) 37 | return callback(err); 38 | 39 | var user = session && session.user; 40 | //console.log("session".greenBG, session) 41 | if (!user) 42 | return callback({error: "Please login", loginRequired: true, logout: true}); 43 | 44 | callback(null, user, session); 45 | }) 46 | } 47 | 48 | dpc(10, function(){ 49 | self.init(); 50 | }); 51 | } 52 | 53 | module.exports = APIBase; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sia-cluster", 3 | "version": "0.9.2", 4 | "description": "Sia Cluster - Remote management & monitoring of Sia hosts", 5 | "author": { 6 | "name": "ASPECTRON Inc.", 7 | "email": "info@aspectron.com" 8 | }, 9 | "private": true, 10 | "scripts": { 11 | "start": "node sia-cluster.js" 12 | }, 13 | "main": "sia-cluster.js", 14 | "dependencies": { 15 | "basic-auth": "*", 16 | "big-integer": "*", 17 | "cloudflare": "*", 18 | "compression": "*", 19 | "ejs": "*", 20 | "iris-app": "^0.1.44", 21 | "iris-i18n": "*", 22 | "iris-mdl": "*", 23 | "iris-polymer": "*", 24 | "iris-rpc": "^0.2.17", 25 | "iris-stats": "*", 26 | "iris-underscore": "*", 27 | "iris-utils": "*", 28 | "mongodb": "^2.1.21", 29 | "morgan": "*", 30 | "multiparty": "*", 31 | "node-uuid": "^1.4.7", 32 | "nodemailer": "*", 33 | "request": "*", 34 | "request-progress": "^2.0.1", 35 | "sia-api": "*", 36 | "underscore": "*", 37 | "xml2json": "*", 38 | "commander": "*", 39 | "readline-sync" : "*" 40 | }, 41 | "engines": { 42 | "node": "*", 43 | "npm": "*" 44 | }, 45 | "repository": { 46 | "type": "git", 47 | "url": "git://github.com/aspectron/sia-cluster" 48 | }, 49 | "bugs": { 50 | "url": "https://github.com/aspectron/sia-cluster/issues" 51 | }, 52 | "license": "MIT", 53 | "homepage": "https://github.com/aspectron/sia-cluster" 54 | 55 | } 56 | -------------------------------------------------------------------------------- /views/site/partial/footer.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 |
9 |

10 |
11 |
12 |
13 | 16 |
17 |
18 |
19 | 20 | 21 |
22 |
23 |
24 |
25 | 26 | 27 | 28 |
29 |
30 | <% if(!req.__scriptAtHeader) { %> 31 | 32 | <% } %> 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /lib/exchanges/yunbi.js: -------------------------------------------------------------------------------- 1 | module.exports = YUNBI; 2 | 3 | var util = require("util"); 4 | var events = require('events'); 5 | var request = require("request"); 6 | var Exchange = require("./exchange"); 7 | var _ = require("underscore"); 8 | 9 | var REQUEST_INTERVAL = 3000; 10 | 11 | function YUNBI(core) { 12 | var self = this; 13 | Exchange.apply(this,arguments); 14 | 15 | // --- 16 | 17 | var baseURL = "https://yunbi.com/api/v2/" 18 | 19 | function fetch(op, callback) { 20 | 21 | self.fetch(baseURL+op, callback); 22 | } 23 | 24 | self.syncTickers = function(callback) { 25 | 26 | var pairs = ["SC/CNY"]; 27 | 28 | var out = { } 29 | 30 | _.asyncMap(pairs, function(pair, callback) { 31 | 32 | var ident = pair.replace('/','').toLowerCase(); 33 | 34 | fetch('tickers/'+ident+'.json', function(err, data) { 35 | //console.log(arguments); 36 | if(err) 37 | return callback(err); 38 | 39 | var o = data.ticker; 40 | 41 | out[pair] = { 42 | ask : parseFloat(o.sell), 43 | bid : parseFloat(o.buy), 44 | volume : parseFloat(o.vol), 45 | high : parseFloat(o.high), 46 | low : parseFloat(o.low) 47 | } 48 | 49 | // dpc(self.ctx.ts ? REQUEST_INTERVAL : 100, callback); 50 | dpc(callback); 51 | 52 | }) 53 | 54 | }, function() { 55 | 56 | self.ctx.tickers = out; 57 | self.ctx.ts = Date.now(); 58 | callback(); 59 | }) 60 | 61 | 62 | } 63 | 64 | } 65 | 66 | util.inherits(YUNBI, events.EventEmitter); 67 | -------------------------------------------------------------------------------- /lib/exchanges/btc-e.js: -------------------------------------------------------------------------------- 1 | module.exports = BTCE; 2 | 3 | var util = require("util"); 4 | var events = require('events'); 5 | var request = require("request"); 6 | var Exchange = require("./exchange"); 7 | var _ = require("underscore"); 8 | 9 | var REQUEST_INTERVAL = 3000; 10 | 11 | function BTCE(core) { 12 | var self = this; 13 | Exchange.apply(this,arguments); 14 | 15 | // --- 16 | 17 | var baseURL = "https://btc-e.com/api/2/" 18 | 19 | function fetch(op, callback) { 20 | 21 | self.fetch(baseURL+op, callback); 22 | } 23 | 24 | self.syncTickers = function(callback) { 25 | 26 | var pairs = ["BTC/USD"]; 27 | 28 | var out = { } 29 | 30 | _.asyncMap(pairs, function(pair, callback) { 31 | 32 | var ident = pair.replace('/','_').toLowerCase(); 33 | 34 | fetch(ident+'/ticker', function(err, data) { 35 | //console.log(arguments); 36 | if(err) 37 | return callback(err); 38 | 39 | var o = data.ticker; 40 | 41 | var volume = [parseFloat(o.vol), parseFloat(o.vol_cur)]; 42 | 43 | out[pair] = { 44 | ask : parseFloat(o.sell), 45 | bid : parseFloat(o.buy), 46 | volume : volume, 47 | high : parseFloat(o.high), 48 | low : parseFloat(o.low) 49 | } 50 | 51 | dpc(self.ctx.ts ? REQUEST_INTERVAL : 100, callback); 52 | 53 | }) 54 | 55 | }, function() { 56 | 57 | self.ctx.tickers = out; 58 | self.ctx.ts = Date.now(); 59 | callback(); 60 | }) 61 | 62 | 63 | } 64 | 65 | } 66 | 67 | util.inherits(BTCE, events.EventEmitter); 68 | -------------------------------------------------------------------------------- /lib/exchanges/kraken.js: -------------------------------------------------------------------------------- 1 | module.exports = KRAKEN; 2 | 3 | var util = require("util"); 4 | var events = require('events'); 5 | var request = require("request"); 6 | var Exchange = require("./exchange"); 7 | var _ = require("underscore"); 8 | 9 | var REQUEST_INTERVAL = 3000; 10 | 11 | function KRAKEN(core) { 12 | var self = this; 13 | Exchange.apply(this,arguments); 14 | 15 | // --- 16 | 17 | var baseURL = "https://api.kraken.com/0/public/Ticker?pair=" 18 | 19 | function fetch(op, callback) { 20 | 21 | self.fetch(baseURL+op, callback); 22 | } 23 | 24 | self.syncTickers = function(callback) { 25 | 26 | var pairs = ["BTC/USD"]; 27 | 28 | var out = { } 29 | 30 | _.asyncMap(pairs, function(pair, callback) { 31 | 32 | var ident = pair.replace('BTC','XBT').replace('/',''); 33 | 34 | fetch(ident, function(err, data) { 35 | //console.log(arguments); 36 | if(err) 37 | return callback(err); 38 | 39 | var resp = _.values(data.result); 40 | if(!resp) 41 | return callback(err); 42 | 43 | var o = resp.shift(); 44 | 45 | out[pair] = { 46 | ask : parseFloat(o.a[0]), 47 | bid : parseFloat(o.b[0]), 48 | volume : [ parseFloat(o.v[1]) ], 49 | high : parseFloat(o.h[1]), 50 | low : parseFloat(o.l[1]) 51 | } 52 | 53 | dpc(self.ctx.ts ? REQUEST_INTERVAL : 100, callback); 54 | 55 | }) 56 | 57 | }, function() { 58 | 59 | self.ctx.tickers = out; 60 | self.ctx.ts = Date.now(); 61 | callback(); 62 | }) 63 | 64 | 65 | } 66 | 67 | } 68 | 69 | util.inherits(KRAKEN, events.EventEmitter); 70 | -------------------------------------------------------------------------------- /lib/exchanges/polo.js: -------------------------------------------------------------------------------- 1 | module.exports = POLO; 2 | 3 | var util = require("util"); 4 | var events = require('events'); 5 | var request = require("request"); 6 | var Exchange = require("./exchange"); 7 | var _ = require("underscore"); 8 | 9 | function POLO(core) { 10 | var self = this; 11 | Exchange.apply(this,arguments); 12 | 13 | // --- 14 | 15 | var baseURL = "https://poloniex.com/public?command=" 16 | 17 | function fetch(op, callback) { 18 | 19 | self.fetch(baseURL+op, callback); 20 | } 21 | 22 | self.syncTickers = function(callback) { 23 | fetch('returnTicker', function(err, data) { 24 | if(err) 25 | return callback(err); 26 | 27 | var out = { } 28 | _.each(data, function(o, pair) { 29 | // console.log(arguments); 30 | 31 | 32 | var impl = pair; 33 | pair = pair.split('_'); 34 | var denom = pair[0]; 35 | var nom = pair[1]; 36 | pair[0] = nom; 37 | pair[1] = denom; 38 | pair = pair.join('/'); 39 | //var chg24h = { } 40 | //chg24h[denom.toLowerCase()] = parseFloat(o.percentChange)*100; 41 | 42 | out[pair] = { 43 | //impl : impl, 44 | //ident : pair, 45 | //chg24h : chg24h, 46 | ask : parseFloat(o.lowestAsk), 47 | bid : parseFloat(o.highestBid), 48 | volume : [ parseFloat(o.baseVolume), parseFloat(o.quoteVolume) ], 49 | high : parseFloat(o.high24hr), 50 | low : parseFloat(o.low24hr) 51 | } 52 | 53 | 54 | }) 55 | 56 | self.ctx.tickers = out; 57 | self.ctx.ts = Date.now(); 58 | callback(null); 59 | }) 60 | 61 | } 62 | 63 | } 64 | 65 | util.inherits(POLO, events.EventEmitter); 66 | -------------------------------------------------------------------------------- /tools/release/bundle: -------------------------------------------------------------------------------- 1 | # !/bin/bash 2 | 3 | pushd . 4 | BUILD_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"; 5 | cd $BUILD_DIR/../.. 6 | 7 | PLATFORM="$1" 8 | VERSION=`cat VERSION` 9 | SIA_CLUSTER_VERSION="$VERSION-$PLATFORM" 10 | 11 | SIA_CLUSTER_RELEASE_PATH="sia-cluster-$SIA_CLUSTER_VERSION" 12 | SIA_NODE_RELEASE_PATH="sia-node-$SIA_CLUSTER_VERSION" 13 | 14 | if [[ ! "$1" =~ ^(win64|linux64|darwin)$ ]]; then 15 | echo "First argument must be a platform: linux64 win64 darwin" 16 | exit 17 | fi 18 | 19 | 20 | if [[ "$2" =~ ^(--local|--dev|--master)$ ]]; then 21 | echo "Building $SIA_CLUSTER_VERSION..." 22 | else 23 | echo "Please use one of the following: --local --dev --master" 24 | exit 25 | fi 26 | 27 | cd .. 28 | mkdir -p releases 29 | cd releases 30 | echo "cwd is:" 31 | pwd 32 | if [[ ! $* == *--nobuild* ]]; then 33 | cd ../sia-cluster/tools/release 34 | bash build $1 $2 35 | if (($? != 0)); then echo "Sia Cluster Build Error" && exit 1; fi 36 | cd ../../../sia-node/tools/release 37 | bash build $1 $2 38 | if (($? != 0)); then echo "Sia Node Build Error" && exit 1; fi 39 | cd ../../../releases 40 | fi 41 | 42 | if [[ ! -d "$SIA_CLUSTER_RELEASE_PATH" ]]; then 43 | echo "Bundle error: Unable to locate $SIA_CLUSTER_RELEASE_PATH" 44 | exit 45 | fi 46 | if [[ ! -d "$SIA_NODE_RELEASE_PATH" ]]; then 47 | echo "Bundle error: Unable to locate $SIA_NODE_RELEASE_PATH" 48 | exit 49 | fi 50 | 51 | echo "Packaging Bundle..." 52 | #/c/Program\ Files/WinRAR/WinRAR a -r sia-cluster-$SIA_CLUSTER_VERSION.rar $SIA_RELEASE_PATH/* 53 | #/c/Program\ Files/WinRAR/WinRAR a -r -afzip sia-cluster-$SIA_CLUSTER_VERSION.zip $SIA_RELEASE_PATH/* 54 | #/c/Program\ Files/Git/mingw64/zip -r sia-cluster-$SIA_CLUSTER_VERSION.zip $SIA_RELEASE_PATH/* 55 | 56 | rm sia-cluster-bundle-$SIA_CLUSTER_VERSION.zip 57 | zip -q -9 -r sia-cluster-bundle-$SIA_CLUSTER_VERSION.zip $SIA_CLUSTER_RELEASE_PATH $SIA_NODE_RELEASE_PATH 58 | 59 | 60 | echo "Done." 61 | 62 | popd 63 | 64 | 65 | -------------------------------------------------------------------------------- /run.js: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2011-2013 ASPECTRON Inc. 3 | // All Rights Reserved. 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | // 23 | 24 | 25 | var _ = require("underscore"), 26 | spawn = require("child_process").spawn, 27 | irisUtils = require("iris-utils"); 28 | 29 | function Application() { 30 | var self = this; 31 | var args = process.argv.slice(2); 32 | 33 | self.process = { } 34 | _.each(args, function (name) { 35 | console.log(('loading: ' + name).bold); 36 | 37 | self.process[name] = new irisUtils.Process({ 38 | process: process.execPath, 39 | args: [ name ], 40 | descr: name, 41 | logger: new irisUtils.Logger({ filename: __dirname + '/logs/' + name + '.log' }) 42 | }); 43 | self.process[name].run(); 44 | }) 45 | } 46 | 47 | global.app = new Application(); -------------------------------------------------------------------------------- /http/app/deps.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /lib/manage/login.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Login 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 33 |
34 |
35 |
Please login:
36 |
37 |
38 | 39 |
40 |
41 |
42 | 43 | 44 | -------------------------------------------------------------------------------- /config/sia-cluster.conf: -------------------------------------------------------------------------------- 1 | // 2 | // Sia Cluster main configuration file 3 | // 4 | // DO NOT EDIT THIS FILE! 5 | // Use sia-cluster.local.conf to override settings 6 | // Refer to https://github.com/aspectron/iris-app#project-configuration 7 | // 8 | 9 | { 10 | certificates : 'certificates/sia-cluster', 11 | rpc : { 12 | // node : { 13 | // port : 58481, 14 | // auth : "1299ece0263565a53df103a34910884d5016a10d86c06e5f309f17761a965d28" 15 | // } 16 | }, 17 | sia : { 18 | host : "http://127.0.0.1:9980" 19 | }, 20 | 21 | http : { 22 | // basicAuth : { 23 | // user : "sia", 24 | // pass : "cluster" 25 | // }, 26 | ssl : false, 27 | redirectToSSL : false, 28 | port : 5566, 29 | // engine : 'ejs', 30 | session : { 31 | // you don't need to change that as this is hashed with UUID of your app instance 32 | secret : "2161b8a773332d6ddd3a4535327c36b97312230b1fc8120eeb5f26045e483263" 33 | } 34 | }, 35 | websocket:{path:'/rpc'}, 36 | mongodb : { main : "mongodb://localhost/sia-cluster" }, 37 | users : { 38 | //"" : "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824" 39 | }, 40 | variables:{ 41 | disableGlobal: [ 42 | "version.version", 43 | "version.platform", 44 | //"^network.lo.rx", 45 | "^storage", 46 | "^network", 47 | "^host\.storage\." 48 | ], 49 | disable: [ 50 | "^system\.loadavg\.*", 51 | "^system\.memory\.*" 52 | ], 53 | stats: [ 54 | "^system", 55 | "^storage.*(bytes|percent)", 56 | "^network.*(bytes|packets|errors)", 57 | "^host\.externalsettings.*(storage|price)", 58 | "^host\.financialmetrics.*(lost|locked|revenue)", 59 | "^host\.networkmetrics.*(consumed)", 60 | "wallet.*siacoinbalance", 61 | ] 62 | }, 63 | statsFlushInterval: 10 * 1000, 64 | statsLogevity: 1000 * 60 * 60 * 24 * 45 // keep stats data for 1.5 months 65 | } 66 | -------------------------------------------------------------------------------- /http/app/css/style.css: -------------------------------------------------------------------------------- 1 | #printableDialog{margin-left: 0px; margin-right: 0px; margin-top: 0px; margin-bottom: 0px; width: 100%;min-height: 100%;background-color: #FFFFFF; font-family: "Open Sans"} 2 | #printableDialog .dialog-contents #scrollable{margin-left: auto; margin-right: auto; padding-bottom: 20px} 3 | #printableDialog .paper-toolbar { padding-left: 0px; } 4 | #printableDialog .title { margin-left: 0px; } 5 | #printableDialog h1, 6 | #printableDialog h2 {text-align: left; font-family: "Open Sans"; font-size: 24px; padding-left: 0px;} 7 | @media print{ 8 | #printableDialog h1, 9 | #printableDialog h2{ 10 | font-size: 18px; 11 | } 12 | } 13 | 14 | .showbox {position: absolute;top: 0px;bottom: 0;left: 0;right: 0;padding: 5%;display: none; z-index: 1000; background-color: #FFF; background-color: rgba(255, 255, 255, 0.5);} 15 | .loading .showbox{display: -ms-flexbox; display: -webkit-flex; display: flex;-ms-flex-direction: row;-webkit-flex-direction: row;flex-direction: row;} 16 | .loader {position: relative;margin: 0px auto;width: 100px;} 17 | .loader:before {content: '';display: block;padding-top: 100%;} 18 | .circular {-webkit-animation: rotate 2s linear infinite;animation: rotate 2s linear infinite;height: 100%;-webkit-transform-origin: center center;transform-origin: center center;width: 100%;position: absolute;top: 0;bottom: 0;left: 0;right: 0;margin: auto;} 19 | .path {-webkit-animation: dash 1.5s ease-in-out infinite;animation: dash 1.5s ease-in-out infinite;stroke-linecap: round;stroke-dasharray: 90,10;stroke: #0057e7;} 20 | @-webkit-keyframes rotate {100% {-webkit-transform: rotate(360deg);}} 21 | @keyframes rotate {100% {transform: rotate(360deg);}} 22 | @-webkit-keyframes dash {0% {stroke-dasharray: 1,200;stroke-dashoffset: 0;}50% {stroke-dasharray: 89,200;stroke-dashoffset: -35;}100% {stroke-dasharray: 89,200;stroke-dashoffset: -124;}} 23 | @keyframes dash {0% {stroke-dasharray: 1,200;stroke-dashoffset: 0;}50% {stroke-dasharray: 89,200;stroke-dashoffset: -35;}100% {stroke-dasharray: 89,200;stroke-dashoffset: -124;}} 24 | 25 | 26 | .paper-input .paper-input-container .input-content label.paper-input { 27 | padding-left: 4px; 28 | color: blue; 29 | /*font-size: 23px;*/ 30 | } 31 | 32 | .iris-overlap-panel-opened iron-overlay-backdrop{background-color:#DDD} 33 | input { 34 | color: black; 35 | } 36 | 37 | 38 | -------------------------------------------------------------------------------- /config/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "cluster": { 4 | "TARGET_USD_TB_MO": 5, 5 | "fields": { 6 | "system.loadavg": { 7 | "showVar": true, 8 | "showGraph": true 9 | }, 10 | "system.memory": { 11 | "showVar": true, 12 | "showGraph": true 13 | }, 14 | "system.version": { 15 | "showVar": true, 16 | "showGraph": false, 17 | "graphCompatible": false 18 | }, 19 | "consensus.height": { 20 | "showVar": true, 21 | "showGraph": false 22 | }, 23 | "wallet.confirmedsiacoinbalance": { 24 | "showVar": true, 25 | "showGraph": false 26 | }, 27 | "host.externalsettings.netaddress": { 28 | "showVar": true, 29 | "showGraph": false 30 | }, 31 | "host.externalsettings.collateral": { 32 | "showVar": true, 33 | "showGraph": false 34 | }, 35 | "host.externalsettings.maxcollateral": { 36 | "showVar": true, 37 | "showGraph": false 38 | }, 39 | "host.externalsettings.contractprice": { 40 | "showVar": true, 41 | "showGraph": false 42 | }, 43 | "host.externalsettings.downloadbandwidthprice": { 44 | "showVar": true, 45 | "showGraph": false 46 | }, 47 | "host.externalsettings.storageprice": { 48 | "showVar": true, 49 | "showGraph": false 50 | }, 51 | "host.externalsettings.uploadbandwidthprice": { 52 | "showVar": true, 53 | "showGraph": false 54 | }, 55 | "host.externalsettings.totalstorage": { 56 | "showVar": true, 57 | "showGraph": false 58 | }, 59 | "host.externalsettings.remainingstorage": { 60 | "showVar": true, 61 | "showGraph": false 62 | }, 63 | "host.externalsettings.usedstorage": { 64 | "showVar": true, 65 | "showGraph": false 66 | } 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /lib/exchanges/ecb.js: -------------------------------------------------------------------------------- 1 | module.exports = ECB; 2 | 3 | var util = require("util"); 4 | var events = require('events'); 5 | var request = require("request"); 6 | var Exchange = require("./exchange"); 7 | var _ = require("underscore"); 8 | var xml2json = require("xml2json"); 9 | 10 | function ECB(core) { 11 | var self = this; 12 | Exchange.apply(this,arguments); 13 | self.last_ts = 0; 14 | 15 | // --- 16 | 17 | var baseURL = "http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml" 18 | 19 | self.syncTickers = function(callback) { 20 | 21 | // skip updates if less than 15 minutes 22 | var ts = Date.now(); 23 | if(ts - self.last_ts < 1000 * 60 * 15) 24 | return callback(); 25 | 26 | self.verbose && console.log("-->",baseURL); 27 | 28 | request({ 29 | url: baseURL, 30 | timeout : 15 * 1000 31 | }, function(err, response, body){ 32 | if(err) 33 | return callback(err); 34 | 35 | if(response.statusCode !== 200) 36 | return callback("Response status code:"+response.statusCode); 37 | 38 | var out = { } 39 | var currencies = ['USD']; 40 | 41 | try { 42 | var json = xml2json.toJson(body, { object : true }) 43 | var data = json['gesmes:Envelope'].Cube.Cube.Cube; 44 | _.each(data, function(o) { 45 | out[o.currency+'/EUR'] = parseFloat(o.rate); 46 | currencies.push(o.currency); 47 | }) 48 | _.each(data, function(o) { 49 | if(o.currency == 'USD') 50 | return; 51 | out[o.currency+'/USD'] = out[o.currency+'/EUR'] / out['USD/EUR']; 52 | }) 53 | 54 | // console.log(out); 55 | // console.log(data); 56 | // console.log(self.rates_EUR); 57 | // console.log(self.rates_USD); 58 | } 59 | catch (ex) { console.log("Error fetching exchange rates:",ex); } 60 | 61 | 62 | self.last_ts = Date.now(); 63 | 64 | self.ctx.tickers = out; 65 | self.ctx.currencies = currencies; 66 | self.ctx.ts = Date.now(); 67 | 68 | callback(null); 69 | }) 70 | 71 | } 72 | 73 | } 74 | 75 | util.inherits(ECB, events.EventEmitter); 76 | -------------------------------------------------------------------------------- /http/app/sia-colors.html: -------------------------------------------------------------------------------- 1 | 2 | 13 | 71 | -------------------------------------------------------------------------------- /lib/helper.js: -------------------------------------------------------------------------------- 1 | var _ = require("underscore"); 2 | var request = require("request"); 3 | var ejs = require("ejs"); 4 | 5 | function Helper(core){ 6 | var self = this; 7 | 8 | self.validateCaptcha = function validateCaptcha(req, callback) { 9 | 10 | var captcha = req.body['g-recaptcha-response']; 11 | if(!captcha || !captcha.length) 12 | return callback({ error : "Captcha validation failure", type : "captcha", "error-codes" : ['invalid-input-response'] }, { success : false }); 13 | 14 | request({ 15 | url: "https://www.google.com/recaptcha/api/siteverify", 16 | method: 'GET', 17 | qs: { 18 | secret: '6Le-XAcTAAAAABRUipByxvc7vIHXVlxHKq4ZikZu', 19 | response: captcha 20 | }, 21 | json: true 22 | }, function(err, response, data) { 23 | 24 | console.log("data",data); 25 | var errors = data['error-codes']; 26 | console.log("errors",errors); 27 | 28 | var error = data.error || ''; 29 | if (errors && errors.length) { 30 | if (_.contains(errors, 'invalid-input-response')) 31 | error = _T("Please complete captcha"); 32 | else 33 | error = errors.join('/'); 34 | } 35 | 36 | if(!data.success) 37 | return callback({ error : error }, data); 38 | callback(null, data); 39 | 40 | }); 41 | } 42 | 43 | self.emitForTokens = function emitForTokens(tokens, eventName, args){ 44 | _.each(tokens, function(token) { 45 | var sockets = core.webSocketTokens[token]; 46 | _.each(sockets, function(socket_id) { 47 | var ws = core.webSocketMap[socket_id]; 48 | if(ws) 49 | ws.emit(eventName, args); 50 | }) 51 | }) 52 | } 53 | self.emitToAll = function emitToAll(eventName, args){ 54 | _.each(_.keys(core.webSocketMap), function(key) { 55 | var ws = core.webSocketMap[key]; 56 | ws && ws.emit(eventName, args); 57 | }) 58 | } 59 | 60 | self.emitToPrivate = function emitToPrivate(eventName, args){ 61 | _.each(core.userWebSocketMap, function(list) { 62 | _.each(list, function(socket_id) { 63 | var ws = core.webSocketMap[socket_id]; 64 | if(ws) 65 | ws.emit(eventName, args); 66 | }) 67 | }) 68 | } 69 | 70 | self.renderFile = function (view, options, callback) { 71 | core.app.render(view, options, function(err, str){ 72 | callback(err, str) 73 | }) 74 | } 75 | 76 | self.renderFileContent = function (content, options) { 77 | var data = _.extend({}, core.app.locals, options || {}); 78 | return ejs.compile(content, {})(data); 79 | } 80 | } 81 | 82 | module.exports = Helper; 83 | -------------------------------------------------------------------------------- /http/app/node-setting-style.html: -------------------------------------------------------------------------------- 1 | 2 | 47 | -------------------------------------------------------------------------------- /views/app/app.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%=_T("SIA Cluster")%> 6 | "/> 7 | 8 | 9 | 10 | 11 | " /> 12 | 13 | 14 | 15 | 16 | 17 | 18 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |
38 |
39 | 40 | 41 | 42 |
43 |
44 | 45 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /config/i18n.conf: -------------------------------------------------------------------------------- 1 | { 2 | "debug": false, 3 | "test": false, 4 | "sourceLanguage": "en", 5 | "folders": [ 6 | "", 7 | "views" 8 | ], 9 | "files": [ 10 | "html", 11 | "js", 12 | "ejs" 13 | ], 14 | "languages": { 15 | "ar": { 16 | "name": "العربية‎", 17 | "enabled": false 18 | }, 19 | "bg": { 20 | "name": "Български", 21 | "enabled": false 22 | }, 23 | "bn": { 24 | "name": "বাংলা", 25 | "enabled": false 26 | }, 27 | "en": { 28 | "name": "English", 29 | "enabled": true 30 | }, 31 | "es": { 32 | "name": "Español", 33 | "enabled": false 34 | }, 35 | "el": { 36 | "name": "Greek", 37 | "enabled": false 38 | }, 39 | "et": { 40 | "name": "Esti", 41 | "enabled": false 42 | }, 43 | "fr": { 44 | "name": "Français", 45 | "enabled": false 46 | }, 47 | "de": { 48 | "name": "Deutsch", 49 | "enabled": false 50 | }, 51 | "da": { 52 | "name": "Danish" 53 | }, 54 | "cs": { 55 | "name": "Czech" 56 | }, 57 | "fa": { 58 | "name": "Farsi" 59 | }, 60 | "fi": { 61 | "name": "Finnish" 62 | }, 63 | "fil": { 64 | "name": "Filipino" 65 | }, 66 | "he": { 67 | "name": "עברית", 68 | "enabled": false 69 | }, 70 | "hi": { 71 | "name": "Hindi", 72 | "enabled": false 73 | }, 74 | "hr": { 75 | "name": "Croatian" 76 | }, 77 | "hu": { 78 | "name": "Hungarian" 79 | }, 80 | "it": { 81 | "name": "Italiano", 82 | "enabled": false 83 | }, 84 | "is": { 85 | "name": "Icelandic", 86 | "enabled": false 87 | }, 88 | "ja": { 89 | "name": "日本語", 90 | "enabled": false 91 | }, 92 | "ko": { 93 | "name": "Korean", 94 | "enabled": false 95 | }, 96 | "lt": { 97 | "name": "Lithuanian" 98 | }, 99 | "nb": { 100 | "name": "Norwegian" 101 | }, 102 | "nl": { 103 | "name": "Dutch" 104 | }, 105 | "no": { 106 | "name": "Norwegian" 107 | }, 108 | "pl": { 109 | "name": "Polski", 110 | "enabled": false 111 | }, 112 | "pt": { 113 | "name": "Português", 114 | "enabled": false 115 | }, 116 | "ro": { 117 | "name": "Romanian", 118 | "enabled": false 119 | }, 120 | "ru": { 121 | "name": "Русский", 122 | "enabled": false 123 | }, 124 | "sk": { 125 | "name": "Slovak" 126 | }, 127 | "sr": { 128 | "name": "Serbian" 129 | }, 130 | "sl": { 131 | "name": "Slovenian" 132 | }, 133 | "sv": { 134 | "name": "Swedish" 135 | }, 136 | "ta": { 137 | "name": "Tamil" 138 | }, 139 | "th": { 140 | "name": "Thai", 141 | "enabled": false 142 | }, 143 | "tr": { 144 | "name": "Turkish", 145 | "enabled": false 146 | }, 147 | "uk": { 148 | "name": "Ukrainian" 149 | }, 150 | "ur": { 151 | "name": "Urdu", 152 | "enabled": false 153 | }, 154 | "vi": { 155 | "name": "Vietnamese" 156 | }, 157 | "mn": { 158 | "name": "Mongolian" 159 | }, 160 | "zh_HANS": { 161 | "name": "中文", 162 | "enabled": false 163 | }, 164 | "zh_HANT": { 165 | "name": "繁體中文", 166 | "enabled": false 167 | } 168 | }, 169 | "language_aliases": { 170 | "en-GB": "en", 171 | "en-US": "en", 172 | "zh-CN": "zh_HANS", 173 | "zh-TW": "zh_HANT" 174 | } 175 | } -------------------------------------------------------------------------------- /tools/release/pubkey: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | xsFNBFenrHgBEAC5phhmOlS9vmvPpBIJb5mo0zOKW796ZYMGiZ4IUi+ofcYj 3 | vH0yiNYH1d5oy19+52cJpE1orOLVjbs++9f6ZcMkCKnBjyuzGDY8WmQvh7QZ 4 | RTKjPZ3qoGBGKyMkjrHwVaxnLkgzRALPoIqwUDnv5v6LlTI81A2Txxgd3q5v 5 | 5qxGIZJMtzDC+NQ1jKytHoM0XBXCmc5tm4Edx4rEJH92OQFbAIo2P+f9asI/ 6 | RyXvYvGzX+z4/SWJyYYPf9HpfwLWALV75j1IKhMc2G78Zbv25M/lGkHXguGL 7 | y127TyivKJjHIkrx93QnESfj2ZSm1Ye6dqOI3nfwvf0lex/4KIk4SIPbBY8P 8 | kMJIi2StW6ZagzskuFgwIQErd4rs+XZ9XRTG8mEOS37EckfdpyZWy6u1MFNk 9 | Smz09VzC9rvQwt5X+2KVVFwVcedvAqrGRHcbfjOm4APCRj2GZZ4+tWer0W+Z 10 | QetWdEHNy7ORDGYFS1hcRnf1NFp9rwkcTSq8Nkx4s/3WFNujsl1Ckf0sW+A/ 11 | UehSn3WOjnyXbYR/1ye1kT8y992BMyKDwxF8TV0bNtBPQ1EnvKoLp8e31yQg 12 | IR3kMwApb91/Mms/aK4rEzUejVBF42sOwcs2b0l+Cwf6kRxPNQPhkCTOfkMs 13 | 3jfVXgRdOUHFnfsBzkgBaUSNgoRfHs+wTLVsMwARAQABzS1BbnRvbiBZZW1l 14 | bHlhbm92IDxhbnRvbi55ZW1lbHlhbm92QGdtYWlsLmNvbT7CwXUEEAEIACkF 15 | AlenrH0GCwkIBwMCCRAgsOGEDKDolQQVCAIKAxYCAQIZAQIbAwIeAQAAKyQQ 16 | ALeh8jFjvk6iTyrxB7CvlNwRQ2Id/46huNAg6+1CHYYgkWTQ1jKrhfInvVC2 17 | vfq3D1ZvkgMwAl2iRNPIUq+eH/2u1qsXs8+n3MtsY4L0neZzqWJGtZP1F3jA 18 | XDpRQG8GZ20IN85lOyX4zkAOYX7gEkiUUhB3NKgrkJBTwuXCxMOv/HDsXm5i 19 | VGESVXW9m4x8geetgT+lbJt4MZboItmTRCiN9W/990bcJZAcozhAeO2R6UNp 20 | JDIDW9plDErcHSDh2OStMjfJpyJkHvcRHwHpr2bQvJy9TgO4VWyccnr515kQ 21 | cNXSocELbYyyJl4HSbRVc7yoskvbBLBA2nLRF9okcmRLOzuXhixvzqb97Ds0 22 | NXFeP9xlf/09BCfvGe5UzeG7LznwD4heuoLI3yUjWxJ7S48ADiXxRwQnW6Eh 23 | Jw9dco4EQaqVXfjWt5M99h5qo3J0FAe0kTYCzrPpdKb4i7NkytGDSKUYggyV 24 | oLcQG7mePtpwt9uCjANk1Vqf98j0JiwA51JK3794bYEz7revh80pP1FTXFFp 25 | nZgjbm5UoXt5BL0bBI6bvN5Sv+oWhSsI3PfRR6q26SE3H7VnKCCCWkh1BZe2 26 | Y4txwYp4j1b3HT/dKplVZgFvskcsT1SJjUTqb/Iws3BKI1VZ0s/lF3pX+yd3 27 | YoYcKkX0C/N9HW9PTSHpMR9ezsFNBFenrHgBEADqkMkKtEMfKLshmW/R6n4Y 28 | RK6Wbla28gzxWh/KMCn74flOWX4bHXAA6TZGicycySRbw1VTTyzMkiKS/7j0 29 | QROUnRQAIOqmixnMNw97aftQRFYDFUJH0DjatvLF94iaXLMwGF6kirQw+g1b 30 | o3mZoMAQne6JK4EfkxqFl43IQ7lFfo8zaYIASvtR3WdTknIo98+W55rt5QFV 31 | YnhNS+9aQO8vpI+QUa6r0sneGmlexNNL5e5qoZvYpvpt96+6kr6hlYTikqd7 32 | QBHBGkrjAPVN0CXl92TW4+2NFdskEopBjX/Qu3u9Bah2pVpMNt1RkP59O3gF 33 | Kql2f8irhrVQBrRtHrveOySMaHa6mxRakJgfBNKmkmhe9ln56qPib6d8zt/B 34 | EwkRqtbPEIiPatW6I6mCsZpvSyEngZrZskxJRRdH6sq14PfaE8v82isQBaWX 35 | MTq2Le6auKy3akXEf7zqIVsSvY1pWNpUpAPcFwTlqCw8EFyr1p5ukUDWlmRN 36 | Hi7hqZ7bxEh9NJKbAMOCv5sBNaVKtnVdPfEznzUpo3gKZsA/t4oiBi58/BrP 37 | 6OQe8BeiRh12MB2Jfyc92XRqfgnk3dQfkzdA95KY27DqheaKIg68XWWZjw82 38 | 0erxPejt2KZ/DvKOb5nFlpD/EMM8EBty/8gSlZ6Stt+Hct7vn77/QZVQKRRJ 39 | 2wARAQABwsFfBBgBCAATBQJXp6x/CRAgsOGEDKDolQIbDAAAoqcP/ik/boaS 40 | Hpav4E3yaY4KfRmnmBuGbXDVB/2uLQtyCmNfANsHCxHo4BJcqvqq3/KPHUsI 41 | yeBxOW98mMR/j5efVerLRcsn1DJn+Tmse3VWj8OL/Mnxd/ha666JoichnSUs 42 | R9ev8Yz96y44IqcCqysc43X58x3UFxVpGNdbWof8I/96oYT7N5uYb6Qvgq4a 43 | VydSzevZZyzuluPd/rPm2Rj8JxtfiGdJpcr8sz4fy2yKsbBblmeYZlPqfRl3 44 | xGabfbhiyP/o6OoREyKRXld/rJ+m+dSnLYFaO1vSvd65J/U8hTxLm9yyfYlf 45 | POPHpl1aFAAg5hliowESQhd7iBcc6GtI1Fi+QQK38MIT7bqzgJimo4IfdIAd 46 | ZzsrnGa18e3SYoSC66G3FtnzyxEErnWt3MTg40wjN4MxPvekgpSMzCkmWEtV 47 | 5ZYNQXxcAAQiFES+Fd8oofI3BIzbuGP3FlLoCdlqGtM/V9D2smxXNNJDUGP7 48 | Z94YfQybuYKPFysholMozo+sKKa8vzJRbkYKevxfoJ9ccF+ZaHKSfbsu2mEp 49 | m6G3cVg6jFanFbKe//Bs5xC0rEn49JNHaaOzfoI6kLxFhjCcd4Dw7BrIvEDu 50 | ytDXZ7Uy3rAAgUw0v0CYxt13A79NfsjAIvNRZfCXjeZ48Bh3H/Y3xTp4W+5T 51 | ef2qpJZiX0X1yRtW 52 | =k9d3 53 | -----END PGP PUBLIC KEY BLOCK----- 54 | 55 | -------------------------------------------------------------------------------- /http/app/sia-network-panel.html: -------------------------------------------------------------------------------- 1 | 2 | 33 | 73 | 74 | -------------------------------------------------------------------------------- /http/app/sia-variable-graph.html: -------------------------------------------------------------------------------- 1 | 2 | 24 | 123 | -------------------------------------------------------------------------------- /tools/release/build: -------------------------------------------------------------------------------- 1 | # !/bin/bash 2 | 3 | pushd . 4 | BUILD_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"; 5 | cd $BUILD_DIR/../.. 6 | 7 | PLATFORM="$1" 8 | VERSION=`cat VERSION` 9 | SIA_CLUSTER_VERSION="$VERSION-$PLATFORM" 10 | SIA_RELEASE_PATH="sia-cluster-$SIA_CLUSTER_VERSION" 11 | MINGW_PATH="/c/Program Files/Git/mingw64" 12 | 13 | if [[ ! "$1" =~ ^(win64|linux64|darwin)$ ]]; then 14 | echo "First argument must be a platform: linux64 win64 darwin" 15 | exit 16 | fi 17 | 18 | 19 | if [[ "$2" =~ ^(--local|--dev|--master|--nobuild)$ ]]; then 20 | echo "Building $SIA_RELEASE_PATH..." 21 | else 22 | echo "Please use one of the following: --local --dev --master" 23 | exit 24 | fi 25 | 26 | cd .. 27 | mkdir -p releases 28 | cd releases 29 | 30 | echo "Cleaning up..." 31 | rm -rf $SIA_RELEASE_PATH 32 | 33 | echo "Cloning..." 34 | 35 | if [[ $* == *--local* ]]; then 36 | echo "Packaging LOCAL copy" 37 | mkdir $SIA_RELEASE_PATH 38 | cp -r ../sia-cluster/* $SIA_RELEASE_PATH/ 39 | cd $SIA_RELEASE_PATH 40 | flatten-packages 41 | elif [[ $* == *--dev* ]]; then 42 | echo "Packaging DEV branch" 43 | git clone https://github.com/aspectron/sia-cluster $SIA_RELEASE_PATH 44 | if (($? != 0)); then echo "GIT CLONE Error" && exit 1; fi 45 | cd $SIA_RELEASE_PATH 46 | git checkout -b dev 47 | if (($? != 0)); then echo "GIT CHECKOUT Error" && exit 1; fi 48 | git branch --set-upstream-to=origin/dev dev 49 | sleep 3 50 | git pull 51 | if (($? != 0)); then echo "GIT PULL Error" && exit 1; fi 52 | npm install 53 | if (($? != 0)); then echo "NPM Error" && exit 1; fi 54 | flatten-packages 55 | elif [[ $* == *--master* ]]; then 56 | echo "Packaging MASTER branch" 57 | git clone https://github.com/aspectron/sia-cluster $SIA_RELEASE_PATH 58 | if (($? != 0)); then echo "GIT CLONE Error" && exit 1; fi 59 | cd $SIA_RELEASE_PATH 60 | npm install 61 | if (($? != 0)); then echo "NPM Error" && exit 1; fi 62 | flatten-packages 63 | fi 64 | 65 | # we are in $SIA_RELEASE_PATH 66 | 67 | echo "Binaries..." 68 | 69 | mkdir -p bin 70 | cd bin 71 | 72 | #mkdir mingw64 73 | #cp -r "$MINGW_PATH/bin/curl.exe" mingw64/ 74 | #cp -r "$MINGW_PATH/bin/libcurl-4.dll" mingw64/ 75 | 76 | mkdir node 77 | 78 | cd .. 79 | mkdir -p data/db 80 | touch data/db/.db 81 | cd bin 82 | 83 | case "$PLATFORM" in 84 | win64) 85 | cp "/c/Program Files/nodejs/node.exe" node/ 86 | echo -e "@echo off\ncd ..\nbin\\\\node\\\\node tools/release/setup.js %*\ncd bin\npause\n" > setup.bat 87 | ;; 88 | linux64) 89 | DIR='DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )";' 90 | cp ~/node/bin/node node/ 91 | echo -e "# !/bin/bash\npushd . > /dev/null\n$DIR\ncd \044DIR/..\nbin/node/node tools/release/setup.js \"$\0100\"\npopd > /dev/null\n" > setup 92 | chmod a+x setup 93 | ;; 94 | darwin) 95 | DIR='DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )";' 96 | cp ~/node/bin/node node/ 97 | echo -e "# !/bin/bash\npushd . > /dev/null\n$DIR\ncd \044DIR/..\nbin/node/node tools/release/setup.js \"$\0100\"\npopd > /dev/null\n" > setup 98 | chmod a+x setup 99 | ;; 100 | esac 101 | 102 | cd ../.. 103 | 104 | if [[ ! $* == *--nopack* ]]; then 105 | echo "Packaging..." 106 | #/c/Program\ Files/WinRAR/WinRAR a -r sia-cluster-$SIA_CLUSTER_VERSION.rar $SIA_RELEASE_PATH/* 107 | #/c/Program\ Files/WinRAR/WinRAR a -r -afzip sia-cluster-$SIA_CLUSTER_VERSION.zip $SIA_RELEASE_PATH/* 108 | #/c/Program\ Files/Git/mingw64/zip -r sia-cluster-$SIA_CLUSTER_VERSION.zip $SIA_RELEASE_PATH/* 109 | rm sia-cluster-$SIA_CLUSTER_VERSION.zip 110 | zip -q -9 -r sia-cluster-$SIA_CLUSTER_VERSION.zip $SIA_RELEASE_PATH/* 111 | fi 112 | 113 | echo "Done." 114 | 115 | popd 116 | 117 | 118 | -------------------------------------------------------------------------------- /views/site/partial/header.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%=_T("SIA Cluster")%> 6 | "/> 7 | 8 | 9 | 10 | 11 | " /> 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | <% if (req.__css){ %> 21 | 22 | <% } %> 23 | 24 | <% if(req.__scriptAtHeader ) { %> 25 | 26 | <% } %> 27 | <% if (req.__loadPolymer) { %> 28 | 29 | <% } %> 30 | <% 31 | var menus = [ 32 | {n: "SIGN-IN", i : 'fingerprint', href:"/login", page:"login", topItem: true, leftItem: true} 33 | ] 34 | req.___menus = menus; 35 | %> 36 | 37 | 38 |
39 |
40 |
41 |
42 |
43 | 44 | 45 |
46 | 47 | 61 |
62 |
63 |
64 | 67 | 79 |
80 |
81 |
"> 82 | 83 | -------------------------------------------------------------------------------- /lib/api.js: -------------------------------------------------------------------------------- 1 | var _ = require("underscore"); 2 | var APIBase = require("./api-base"); 3 | var crypto = require("crypto"); 4 | 5 | function API(core){ 6 | var self = this; 7 | APIBase.call(self, core, "API"); 8 | self.clientIPs = {}; 9 | 10 | self.init = function(){} 11 | 12 | self.initHttp = function(app){ 13 | 14 | app.get('/', function(req, res, next) { 15 | if (!req.session.user) 16 | return res.redirect("/login"); 17 | 18 | var user = { 19 | email: req.session.user.email 20 | } 21 | res.render("app", { req: req, user: user }); 22 | }); 23 | 24 | app.get('/login', function(req, res, next) { 25 | req.session.challenge = crypto.createHash('sha256').update(core.config.http.session.secret+Date.now()).digest('hex'); 26 | res.render("login", { req: req, user: false }); 27 | }); 28 | 29 | app.get('/logout', function(req, res, next) { 30 | delete req.session.user; 31 | res.redirect("/"); 32 | }); 33 | 34 | app.post('/login', function(req, res, next) { 35 | 36 | var ip = core.getClientIp(req); 37 | var ts = Date.now(); 38 | var user = req.body.user; 39 | var sig = req.body.sig; 40 | var challenge = req.session.challenge; 41 | if(!user || !sig || !user.length || !sig.length || !challenge) 42 | return res.status(401).json({error: "Invalid details"}); 43 | 44 | var info = self.clientIPs[ip]; 45 | if(!info) { 46 | info = self.clientIPs[ip] = { 47 | ts : ts, 48 | hits : 0 49 | } 50 | } 51 | else { 52 | info.hits++; 53 | } 54 | 55 | var next = info.ts + info.hits * 1000; 56 | if(next > ts) { 57 | return res.status(200).json({ ack : "Too Many Attempts - Please wait "+((next-ts)/1000).toFixed()+" seconds before trying again.."}); 58 | } 59 | else { 60 | info.ts = ts; 61 | var userId = user; 62 | user = core.config.users[userId]; 63 | if(!user || !user.pass) 64 | return res.status(401).end(); 65 | 66 | var lsig = crypto.createHmac('sha256', new Buffer(challenge, 'hex')).update(new Buffer(user.pass, 'hex')).digest('hex'); 67 | 68 | console.log("lsig", lsig, sig) 69 | if(sig != lsig) 70 | return res.status(401).end(); 71 | user.uuid = userId; 72 | req.session.user = user; 73 | return res.status(200).json({ ack : challenge }); 74 | } 75 | }) 76 | /* 77 | app.post('/contact', function(req, res, next){ 78 | var data = req.body; 79 | var config = core.config.contactus.email; 80 | core.helper.renderFile("contactus", {data: data}, function(err, html){ 81 | 82 | var mailOptions = { 83 | from: config.from, // sender address 84 | to: config.to, // list of receivers 85 | replyTo: data.email, 86 | subject: config.subject.replace('{name}', data.name), // Subject line 87 | text: config.text.replace(/\{name\}/g, data.name).replace(/\{email\}/g, data.email).replace(/\{message\}/g, data.message), // plaintext body 88 | html: html // html body 89 | }; 90 | 91 | core.mailer.sendMail(mailOptions, function(err, info){ 92 | if(err){ 93 | console.log(err); 94 | return res.status(500).json({error: "Please try later"}); 95 | } 96 | 97 | //console.log('Contact-Us E-Mail Message sent: ' + info.response); 98 | res.json({success: true, message: req._T( config.alertMsg )}); 99 | }); 100 | }) 101 | }); 102 | */ 103 | } 104 | } 105 | 106 | module.exports = API; -------------------------------------------------------------------------------- /http/app/app-style.html: -------------------------------------------------------------------------------- 1 | 2 | 92 | -------------------------------------------------------------------------------- /http/app/app.js: -------------------------------------------------------------------------------- 1 | 2 | App = _.extend(window.App || {}, { 3 | dpc : function(t,fn) { if(typeof(t) == 'function') setTimeout(t,0); else setTimeout(fn,t); }, 4 | stripC : function(v) { return (typeof v == 'string') ? parseFloat(v.replace(',','')) : (v || 0) }, 5 | c : function(f, precision) { 6 | if(f === null || f === undefined) 7 | return '?'; 8 | return f.toFixed(precision).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); 9 | }, 10 | error : function(err, title, callback) { 11 | if (err.logout){ 12 | IRIS.Alert({title: "Auto Sign-out", text: "Please Sign-in."}, function(btn){ 13 | window.location.href = '/login?redirect_uri=' + encodeURIComponent(window.location.href); 14 | }); 15 | return 16 | } 17 | if (err){ 18 | if (err.error) { 19 | IRIS.Alert({title: title || "Error", text:err.error}, callback) 20 | }else if (err.warning) { 21 | IRIS.Alert({title: title || "Warning", text:err.warning}, callback) 22 | }else if (err.info) { 23 | IRIS.Alert({title: title || "Info", text:err.info}, callback) 24 | } 25 | }; 26 | return err; 27 | }, 28 | 29 | showLoading: function(){ 30 | $(document.body).addClass('loading') 31 | }, 32 | hideLoading: function(){ 33 | $(document.body).removeClass('loading') 34 | }, 35 | _graphRange: 24, 36 | setGraphRange: function(hours){ 37 | if (this._graphRange+"" == hours+"") 38 | return; 39 | this._graphRange = hours; 40 | $(document.body).trigger("graph-range-changed", {graphRange: this._graphRange}); 41 | }, 42 | getGraphRange: function(){ 43 | var now = Date.now(); 44 | if (_.isArray(this._graphRange)) { 45 | var endTS = now - this._graphRange[1]; 46 | var startTS = now - this._graphRange[0]; 47 | return {endTS: endTS, startTS: startTS}; 48 | }; 49 | 50 | var endTS = now; 51 | var startTS = endTS - this._graphRange * 60 * 60 * 1000; 52 | return {endTS: endTS, startTS: startTS}; 53 | }, 54 | str2hex: function (str, isNumber) { 55 | if (isNumber) 56 | return parseInt(str).toString(16); 57 | 58 | var tempstr = ''; 59 | str = str+""; 60 | for (a = 0; a < str.length; a++) { 61 | tempstr = tempstr + str.charCodeAt(a).toString(16); 62 | } 63 | return tempstr; 64 | }, 65 | hex2str: function (str, isNumber) { 66 | if (isNumber) 67 | return parseInt(str, 16); 68 | 69 | var tempstr = ''; 70 | for (b = 0; b < str.length; b = b + 2) { 71 | tempstr = tempstr + String.fromCharCode(parseInt(str.substr(b, 2), 16)); 72 | } 73 | return tempstr; 74 | } 75 | }) 76 | 77 | var VariableDataFormatter = { 78 | load: function(value, field, data){ 79 | if(value === undefined) 80 | return 'N/A'; 81 | if (!_.isObject(value)) 82 | return parseFloat(value).toFixed(2); 83 | return _.map(value, function(ln) { 84 | return parseFloat(ln).toFixed(2); 85 | }).join('/'); 86 | }, 87 | memory: function(value, field, data) { 88 | if (!_.isObject(value)) 89 | return value ? parseInt(value).toFileSize() : ""; 90 | 91 | return this.memory(value.used) +"/"+ this.memory(value.total) 92 | }, 93 | systemVersion: function(value, field, data){ 94 | if (!data || !data.version) 95 | return ""; 96 | return data.version.version + " " + data.version.platform; 97 | } 98 | } 99 | var VariableGraphFormatter = { 100 | _getYTitle: function(variable, y){ 101 | var t = NodeVariableSettings.info; 102 | if (!t[variable]) 103 | return y; 104 | if (t[variable][y]) 105 | return t[variable][y]; 106 | if (t[variable+"."+y]) 107 | return t[variable+"."+y].title; 108 | return y; 109 | }, 110 | _forEachSeries: function(row, variable, fn){ 111 | var d = {x: row.x}; 112 | 113 | var self = this; 114 | _.each(row, function(v, y){ 115 | if (y == "x") 116 | return 117 | d[self._getYTitle(variable, y)] = fn(v, y); 118 | }) 119 | return d; 120 | }, 121 | memory: function(row, index, variable){ 122 | return this._forEachSeries(row, variable, function(v, y){ 123 | return v / 1024 / 1024 / 1024; 124 | }) 125 | }, 126 | SC_BTC: function(row, index, variable){ 127 | return this._forEachSeries(row, variable, function(v, y){ 128 | return v * 1e6; 129 | }) 130 | }, 131 | SC_USD: function(row, index, variable){ 132 | return this._forEachSeries(row, variable, function(v, y){ 133 | return v * 100; 134 | }) 135 | } 136 | }; 137 | 138 | String.prototype.replaceAt=function(index, character) { 139 | return this.substr(0, index) + character + this.substr(index+character.length); 140 | } 141 | String.prototype.pad=function(length, character) { 142 | var text = this; 143 | while(text.length < length) 144 | text = character+text; 145 | return text; 146 | } -------------------------------------------------------------------------------- /http/images/sia-cluster.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /http/app/sia-pricing.html: -------------------------------------------------------------------------------- 1 | 2 | 65 | 146 | -------------------------------------------------------------------------------- /http/app/sia-cluster-storage.html: -------------------------------------------------------------------------------- 1 | 2 | 63 | 162 | -------------------------------------------------------------------------------- /views/site/login.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Login 7 | 8 | 9 | 10 | 11 | 12 | 13 | 54 | 94 | 95 | 96 | 97 |
98 | 99 |
100 |
101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 |
114 | 115 | 116 | 117 | 120 | 123 | 124 | 125 | 128 | 131 | 132 | 135 | 136 | 137 | 139 | 140 |
118 | Username: 119 | 121 | 122 |
126 | Password: 127 | 129 | 130 |
133 |
Login
134 |
138 |
141 |
142 | 143 | 144 |
145 | 146 | -------------------------------------------------------------------------------- /http/app/sia-network-peers-panel.html: -------------------------------------------------------------------------------- 1 | 2 | 66 | 179 | -------------------------------------------------------------------------------- /lib/manage/lib/manage.js: -------------------------------------------------------------------------------- 1 | var ManageBase = require("./manage-base"); 2 | var CloudFlare = require("cloudflare"); 3 | var zutils = require('iris-utils'); 4 | var _ = require('iris-underscore'); 5 | var util = require('util'); 6 | var UUID = require('node-uuid'); 7 | var path = require('path'); 8 | var fs = require('fs'); 9 | 10 | function Manage(core) { 11 | var self = this; 12 | ManageBase.call(this, core); 13 | 14 | 15 | self.on('init-http', function(app) { 16 | 17 | if(core.config.cloudflare) { 18 | console.log("Creating CloudFlare Interface"); 19 | self.cf = CloudFlare.createClient({ 20 | email : core.config.cloudflare.email, 21 | token : core.config.cloudflare.token 22 | }) 23 | }; 24 | 25 | /* 26 | app.post('/manage/file', function (req, res, next) { 27 | function buildPath(fields, files){ 28 | //var pid = fields.pid[0]; 29 | var name = files.file[0].originalFilename.replace(/ /g, '-'); 30 | console.log("fields".greenBG, fields, name) 31 | return self.getPapersPdfPath(name); 32 | } 33 | self.uploadFile({req: req, buildPath: buildPath}, function(err, result){ 34 | if (err) 35 | return self.sendResponce(res, err); 36 | 37 | self.sendResponce(res, null, {success:true, message: "File Upload Complete"}); 38 | var name = result.file.split('/').pop(); 39 | self.webSockets.emit('message', { op : 'file-inserted', data: {name:name, pdf: name, uuid: UUID.v1()} }); 40 | }); 41 | }); 42 | */ 43 | 44 | }); 45 | 46 | self.on('get-app-data', function(args, callback) { 47 | callback(null, { 48 | uuid : core.uuid, 49 | name : core.pkg.name, 50 | monitor : core.config.monitor 51 | }) 52 | }); 53 | 54 | self.on('trace-http', function(args, callback) { 55 | core.traceHttp = !core.traceHttp; 56 | console.log('HTTP Logging',core.traceHttp?'ON'.green.bold:'OFF'.magenta.bold); 57 | callback(null, 'Done'); 58 | }) 59 | 60 | self.on('cf-reset', function(args, callback) { 61 | if(!self.cf) 62 | return callback("No CloudFlare"); 63 | 64 | console.log("Clearing CF Cache..."); 65 | self.cf.clearCache('scalingbitcoin.org', function() { 66 | console.log("CF Cache Cleared..."); 67 | callback(null,"CF Cache Cleared..."); 68 | }) 69 | }) 70 | 71 | self.on('git-pull', function(args, callback){ 72 | //console.log(args.op.greenBG, args, process.execPath) 73 | 74 | var logger = new zutils.Logger({ filename: core.appFolder + '/logs/git_pull.log' }); 75 | var gitPullRequest = new zutils.Process({ 76 | process: '/usr/bin/git', 77 | args: [ 'pull' ], 78 | descr: 'git-pull', 79 | restart: false, 80 | logger: logger 81 | }); 82 | var write = logger.write; 83 | var data = ''; 84 | logger.write = function(text){ 85 | write.call(logger, text); 86 | data += text; 87 | } 88 | gitPullRequest.run(); 89 | gitPullRequest.relaunch = false; 90 | gitPullRequest.process.on('exit',function (code) { 91 | //console.log("data:".greenBG, code, data) 92 | delete logger; 93 | if (code != 0) 94 | return callback({error: 'Please try again.', data: data}) 95 | 96 | callback(null, data); 97 | }); 98 | }); 99 | 100 | self.buildItem = function(cNameOrC, item, callback){ 101 | delete item._id; 102 | /*if (cNameOrC == 'papers') { 103 | if (item.date) { 104 | var date = new Date(item.date); 105 | item.date = date.format('Y-m-d'); 106 | }; 107 | };*/ 108 | callback(); 109 | } 110 | 111 | /*self.defineFetchDataHandler(self, 'users', 'users', function(d){ 112 | d.isFiltered = !_.isEmpty(d.query); 113 | d.query._internal = {$exists: false}; 114 | return d; 115 | });*/ 116 | 117 | /*self.insertUser = function(data, callback){ 118 | if (!data.uuid) 119 | data.uuid = UUID.v1(); 120 | 121 | core.db.users.insert(data, function(err, records){ 122 | if (err) 123 | return callback(err); 124 | 125 | var record = records.pop? records.pop(): records.ops.pop(); 126 | 127 | if (!record) 128 | return callback({error: 'Unable to save. Please try again.'}); 129 | 130 | callback(null, record); 131 | self.webSockets.emit('message', { op : 'user-inserted', data: record }); 132 | //self.syncApp({action:'user-inserted', data: record}); 133 | }); 134 | } 135 | 136 | self.updateUser = function(uuid, data, callback){ 137 | core.db.users.update({uuid: uuid}, {$set: data}, function(err, result){ 138 | if (err) 139 | return callback(err); 140 | 141 | callback(null, {uuid: uuid}); 142 | data.uuid = uuid; 143 | 144 | self.webSockets.emit('message', { op : 'user-updated', data: data }); 145 | //self.syncApp({action:'attendee-updated', data: data}); 146 | }); 147 | } 148 | 149 | self.on('save-user', function(args, callback){ 150 | var d = {}, data = args.data; 151 | 152 | _.each([ 153 | 'email', 154 | 'name', 155 | 'accountType', 156 | 'alias', 157 | 'img' 158 | ], function(n) { 159 | if(data[n]) 160 | d[n] = data[n]; 161 | }); 162 | 163 | d.active = !!data.active; 164 | d.premium = !!data.premium; 165 | 166 | if (data.uuid && data.uuid.length) { 167 | self.updateUser(data.uuid, d, callback); 168 | }else{ 169 | var _hash = self.users.createHash(Date.now()+Math.random()+'dpts2016'); 170 | d = _.extend(d, { 171 | uuid: UUID.v1(), 172 | _hash: _hash, 173 | hash: self.users.createHash(args.email.toLowerCase() + _hash), 174 | tshash: self.users.createHash(Date.now()+Math.random() + _hash), 175 | confirm: true 176 | }); 177 | self.insertUser(d, function (err, record) { 178 | if (err) 179 | return callback(err); 180 | 181 | callback(null, record); 182 | }); 183 | } 184 | });*/ 185 | 186 | } 187 | util.inherits(Manage, ManageBase); 188 | 189 | module.exports = Manage; 190 | -------------------------------------------------------------------------------- /sia-cluster.js: -------------------------------------------------------------------------------- 1 | var IRISApp = require('iris-app'); 2 | var IRISPolymer = require('iris-polymer'); 3 | var IRISMDL = require('iris-mdl'); 4 | var _ = require('iris-underscore'); 5 | 6 | var i18n = require('iris-i18n'); 7 | var util = require('util'); 8 | var path = require("path"); 9 | var morgan = require("morgan"); 10 | var basicAuth = require('basic-auth'); 11 | var compression = require('compression'); 12 | var crypto = require('crypto'); 13 | var program = require('commander'); 14 | 15 | var Manage = require('./lib/manage'); 16 | var API = require('./lib/api'); 17 | var Helper = require('./lib/helper'); 18 | 19 | var Nexus = require('./lib/nexus'); 20 | 21 | function SIACluster() { 22 | var self = this; 23 | IRISApp.Application.apply(this, arguments); 24 | 25 | if(!_.keys(self.config.rpc).length) { 26 | console.log("Error: You must configure RPC settings in".red.bold,"sia-cluster.local.conf".bold); 27 | console.log("You may need to copy","sia-cluster.local.conf-example".bold,"to","sia-cluster.local.conf".bold); 28 | process.exit(); 29 | } 30 | 31 | program 32 | .usage("[options]") 33 | .option('-v, --verbose','Set info verbose mode') 34 | .option('-d, --debug','Set debug verbose mode') 35 | .parse(process.argv); 36 | self.verbose = program.debug ? 2 : (program.verbose ? 1 : 0); 37 | self.verbose && console.log("Setting verbose mode to".yellow.bold,self.verbose); 38 | 39 | 40 | self.nexus = new Nexus(self); 41 | 42 | self.i18n = new i18n(self); 43 | self.manage = new Manage(self); 44 | self.api = new API(self); 45 | self.helper = new Helper(self); 46 | self.httpCombiner = new IRISApp.HttpCombiner(self, { 47 | //prefix: 'combine:', 48 | //debug: true, 49 | inlineCss: true, 50 | inlineScript: true, 51 | folders: [ 52 | self.appFolder+'/http/', 53 | self.appFolder+'/http/app/', 54 | self.appFolder+'/http/app/css/', 55 | self.appFolder+'/http/app/scripts/', 56 | self.appFolder+'/http/site/css/', 57 | self.appFolder+'/http/site/scripts/', 58 | self.appFolder+'/lib/manage/resources/' 59 | ] 60 | }); 61 | //self.mailer = new Mailer(self); 62 | self.irisPolymer = new IRISPolymer(self, { 63 | httpCombiner: self.httpCombiner 64 | }); 65 | self.irisMDL = new IRISMDL(self, { 66 | httpCombiner: self.httpCombiner 67 | }); 68 | 69 | self.webSocketTokens = {}; 70 | 71 | self.databaseConfig = [{ 72 | config: 'main', 73 | collections: [ 74 | {collection: "nodes", indexes: "_id"} 75 | ] 76 | }]; 77 | 78 | 79 | self.updateTweets = function(tweets) { 80 | self.app.locals.tweets = tweets; 81 | } 82 | 83 | 84 | self.on('init::express', function() { 85 | 86 | self.app.disable('x-powered-by'); 87 | self.app.use(function(req, res, next) { 88 | res.setHeader("X-Powered-By", "SIACluster"); 89 | next(); 90 | }) 91 | 92 | if(self.config.http.basicAuth) { 93 | self.app.use(function(req, res, next) { 94 | var auth = basicAuth(req); 95 | if(!auth || auth.name != self.config.http.basicAuth.user || auth.pass != self.config.http.basicAuth.pass) { 96 | res.writeHead(401, { 'WWW-Authenticate': 'Basic realm="Please login"' }); 97 | return res.end(); 98 | } 99 | next(); 100 | }); 101 | } 102 | 103 | self.i18n.initHttp(self.app); 104 | self.manage.initHttp(self.app); 105 | self.httpCombiner.initHttp(self.app); 106 | self.irisPolymer.initHttp(self.app); 107 | self.irisMDL.initHttp(self.app); 108 | self.api.initHttp(self.app); 109 | 110 | self.app.locals._ = _; 111 | self.app.locals.activePage = "home"; 112 | self.app.locals.baseUrl = self.config.baseUrl; 113 | 114 | self.app.locals.tweets = null; 115 | 116 | self.traceHttp = false; 117 | var logger = morgan('dev'); 118 | 119 | self.app.use(function(req, res, next) { 120 | if(self.traceHttp) 121 | return logger(req, res, next); 122 | next(); 123 | }) 124 | 125 | self.app.set('views', self.app.get('views').concat([ 126 | path.join(self.appFolder,'/views/app'), 127 | path.join(self.appFolder,'/views/site') 128 | ])); 129 | 130 | self.app.use(compression({filter: shouldCompress})) 131 | 132 | function shouldCompress(req, res) { 133 | if (req.headers['x-no-compression']) { 134 | // don't compress responses with this request header 135 | return false 136 | } 137 | 138 | // fallback to standard filter function 139 | return compression.filter(req, res) 140 | } 141 | 142 | self.app.use(self.express.static(path.join(self.appFolder, 'http'))); 143 | 144 | self.app.use('*', function(req, res, next) { 145 | res.status(404).render('error', { 146 | message: req._T("Error 404: Not Found") 147 | }); 148 | }); 149 | 150 | self.app.use(function(err, req, res, next) { 151 | console.error((err instanceof Error) ? err.stack : err); 152 | res.status(500).render('error', { 153 | message: req._T ? req._T("Site under maintenance, please check back later.") : "Site under maintenance, please check back later." 154 | }); 155 | }); 156 | }); 157 | 158 | self.initUserWebSocketMaping = function(){ 159 | self.userWebSocketMap = {}; 160 | self.on("websocket::connect", function(socket){ 161 | getUserId(socket, function(err, uuid){ 162 | if (!self.userWebSocketMap[uuid]) 163 | self.userWebSocketMap[uuid] = []; 164 | 165 | self.userWebSocketMap[uuid].push(socket.id); 166 | 167 | // dpc(function() { self.nexus.emit('ws::connect', socket); }); 168 | }) 169 | }); 170 | self.on("websocket::disconnect", function(socket){ 171 | getUserId(socket, function(err, uuid){ 172 | if (!self.userWebSocketMap[uuid]) 173 | return; 174 | 175 | self.userWebSocketMap[uuid] = _.filter(self.userWebSocketMap[uuid], function(s){ 176 | return socket.id != s.id; 177 | }); 178 | }); 179 | }); 180 | 181 | function getUserId(socket, callback){ 182 | self.getSocketSession(socket, function(err, session){ 183 | if (err || !session) 184 | return; 185 | 186 | var uuid = session.user ? (session.user.uuid || session.user._id): false; 187 | if (!uuid) 188 | return; 189 | 190 | callback(null, uuid) 191 | }); 192 | } 193 | } 194 | 195 | self.initUserWebSocketMaping(); 196 | } 197 | 198 | 199 | 200 | util.inherits(SIACluster, IRISApp.Application); 201 | new SIACluster(__dirname); 202 | 203 | -------------------------------------------------------------------------------- /http/scripts/chart.js: -------------------------------------------------------------------------------- 1 | 2 | function pieChart(holder, data, height){ 3 | var $holder = d3.select(holder); 4 | var width = $holder.node().clientWidth; 5 | height = height || 450; 6 | var radius = Math.min(width, height) / 2.5; 7 | $holder.style('height', height+"px") 8 | 9 | 10 | var svgRoot = d3.select(holder) 11 | .append("svg") 12 | .attr("width", width) 13 | .attr("height", height) 14 | .call(glow("shadow").rgb("#555")) 15 | 16 | var svg = svgRoot.append("g"); 17 | 18 | svg.append("g").attr("class", "slices"); 19 | svg.append("g").attr("class", "labels"); 20 | svg.append("g").attr("class", "lines"); 21 | 22 | 23 | var pie = d3.layout.pie() 24 | .sort(null) 25 | .value(function(d) { 26 | return d.value; 27 | }); 28 | 29 | var arc = d3.svg.arc() 30 | .outerRadius(radius * 0.8) 31 | .innerRadius(radius * 0); 32 | 33 | var outerArc = d3.svg.arc() 34 | .outerRadius(radius * 0.9) 35 | .innerRadius(radius * 0.9) 36 | 37 | svg.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")"); 38 | 39 | var key = function(d){ return d.data.label; }; 40 | 41 | var color = d3.scale.ordinal() 42 | //.domain(["Lorem ipsum", "dolor sit", "amet", "consectetur", "adipisicing", "elit", "sed", "do", "eiusmod", "tempor", "incididunt"]) 43 | .range(["#7bee88", "#98abc5", "#98ab05", "#8a89a6", "#7b6888", "#EEE", "#DDD", "#6b486b", "#a05d56", "#d0743c", "#3DC1FB", "#B4E9FE", "#ff8c00"]); 44 | 45 | function updateLabels(){ 46 | var total = 0; 47 | data.map(function(v){ 48 | total += v.value; 49 | }); 50 | return data.map(function(v){ 51 | var s = parseFloat(v.value * 100 / total).toFixed(0); 52 | return { label: v.text + " ("+s+"%)", value: v.value } 53 | }); 54 | } 55 | 56 | change(updateLabels()); 57 | 58 | 59 | function change(data) { 60 | 61 | /* ------- PIE SLICES -------*/ 62 | var slice = svg.select(".slices").selectAll("path.slice") 63 | .data(pie(data), key); 64 | 65 | slice.enter() 66 | .insert("path") 67 | .attr("d", arc) 68 | .style("fill", function(d) { return color(d.data.label); }) 69 | .attr("class", "slice"); 70 | 71 | slice 72 | .transition().duration(0) 73 | .attrTween("d", function(d) { 74 | this._current = this._current || d; 75 | var interpolate = d3.interpolate(this._current, d); 76 | this._current = interpolate(0); 77 | return function(t) { 78 | return arc(interpolate(t)); 79 | }; 80 | }) 81 | 82 | slice.exit() 83 | .remove(); 84 | 85 | /* ------- TEXT LABELS -------*/ 86 | 87 | var text = svg.select(".labels").selectAll("g") 88 | .data(pie(data), key); 89 | 90 | var g = text.enter() 91 | .append("g"); 92 | 93 | 94 | g.append("rect") 95 | .attr("width", "90") 96 | .attr("height", "24") 97 | .attr("stroke", "#111") 98 | .attr("stroke-width", "1") 99 | .attr("fill", "#FFF") 100 | .style("filter", "url(#shadow)") 101 | 102 | g.append("text") 103 | .attr("dy", ".35em") 104 | .text(function(d) { 105 | return d.data.label; 106 | }); 107 | 108 | 109 | 110 | function midAngle(d){ 111 | return d.startAngle + (d.endAngle - d.startAngle)/2; 112 | } 113 | 114 | text.transition().duration(0) 115 | .attrTween("transform", function(d, a) { 116 | this._current = this._current || d; 117 | var interpolate = d3.interpolate(this._current, d); 118 | this._current = interpolate(0); 119 | var me = this; 120 | 121 | return function(t) { 122 | var rect = d3.select(me).select('rect'); 123 | var text = d3.select(me).select('text'); 124 | var d2 = interpolate(t); 125 | var pos = outerArc.centroid(d2); 126 | var cc = (midAngle(d2) < Math.PI ? 1 : -1); 127 | pos[0] = radius * cc; 128 | if (text.node() && rect.node()) { 129 | console.dir(text.node()) 130 | var textWidth = text.style("width").replace("px", ""); 131 | textWidth = parseFloat(textWidth); 132 | var pos2 = [ ]; 133 | if (cc < 0) { 134 | pos2[0] = - textWidth - 8; 135 | }else{ 136 | pos2[0] = -8; 137 | } 138 | 139 | pos2[1] = -12; 140 | rect.attr("transform", "translate("+ pos2 +")"); 141 | rect.attr("width", textWidth + 16); 142 | } 143 | return "translate("+ pos +")"; 144 | }; 145 | }) 146 | .styleTween("text-anchor", function(d){ 147 | this._current = this._current || d; 148 | var interpolate = d3.interpolate(this._current, d); 149 | this._current = interpolate(0); 150 | return function(t) { 151 | var d2 = interpolate(t); 152 | return midAngle(d2) < Math.PI ? "start":"end"; 153 | }; 154 | }); 155 | 156 | text.exit() 157 | .remove(); 158 | 159 | 160 | var polyline = svg.select(".lines").selectAll("polyline") 161 | .data(pie(data), key); 162 | 163 | polyline.enter() 164 | .append("polyline"); 165 | 166 | polyline.transition().duration(1000) 167 | .attrTween("points", function(d){ 168 | this._current = this._current || d; 169 | var interpolate = d3.interpolate(this._current, d); 170 | this._current = interpolate(0); 171 | return function(t) { 172 | var d2 = interpolate(t); 173 | var pos = outerArc.centroid(d2); 174 | pos[0] = radius * 0.95 * (midAngle(d2) < Math.PI ? 1 : -1); 175 | return [arc.centroid(d2), outerArc.centroid(d2), pos]; 176 | }; 177 | }); 178 | 179 | polyline.exit() 180 | .remove(); 181 | }; 182 | } 183 | 184 | function glow(url) { 185 | var stdDeviation = 5, 186 | rgb = "#1F75C4", 187 | colorMatrix = "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0"; 188 | 189 | if (!arguments.length) { 190 | url = "glow"; 191 | } 192 | 193 | function my() { 194 | 195 | var defs = this.append("defs"); 196 | 197 | var filter = defs.append("filter") 198 | .attr("id", url) 199 | .attr("x", "0%") 200 | .attr("y", "0%") 201 | .attr("width", "110%") 202 | .attr("height", "120%") 203 | .call(function() { 204 | this.append("feColorMatrix") 205 | .attr("type", "matrix") 206 | .attr("values", colorMatrix); 207 | this.append("feGaussianBlur") 208 | .attr("stdDeviation", stdDeviation) 209 | .attr("result", "coloredBlur"); 210 | }); 211 | 212 | filter.append("feMerge") 213 | .call(function() { 214 | this.append("feMergeNode") 215 | .attr("in", "coloredBlur"); 216 | this.append("feMergeNode") 217 | .attr("in", "SourceGraphic"); 218 | }); 219 | } 220 | 221 | my.rgb = function(value) { 222 | if (!arguments.length) return color; 223 | rgb = value; 224 | color = d3.rgb(value); 225 | var matrix = "0 0 0 red 0 0 0 0 0 green 0 0 0 0 blue 0 0 0 1 0"; 226 | colorMatrix = matrix 227 | .replace("red", color.r/256) 228 | .replace("green", color.g/256) 229 | .replace("blue", color.b/256); 230 | return my; 231 | }; 232 | 233 | my.stdDeviation = function(value) { 234 | if (!arguments.length) return stdDeviation; 235 | stdDeviation = value; 236 | return my; 237 | }; 238 | 239 | return my; 240 | } 241 | function filter() { 242 | 243 | var defs = this.append("defs"); 244 | 245 | var filter = defs.append("filter") 246 | .attr("id", "shadow") 247 | .attr("x", "0") 248 | .attr("y", "0") 249 | .attr("width", "140%") 250 | .attr("height", "140%") 251 | .call(function() { 252 | this.append("feOffset") 253 | .attr("result", "offOut") 254 | .attr("in", "SourceGraphic") 255 | .attr("dx", "20") 256 | .attr("dy", "20"); 257 | 258 | this.append("feGaussianBlur") 259 | .attr("result", "blurOut") 260 | .attr("in", "offOut") 261 | .attr("stdDeviation", "10"); 262 | 263 | this.append("feBlend") 264 | .attr("mode", "normal") 265 | .attr("in", "SourceGraphic") 266 | .attr("in2", "blurOut"); 267 | }); 268 | } 269 | 270 | -------------------------------------------------------------------------------- /http/api.js: -------------------------------------------------------------------------------- 1 | function API($){ 2 | var self = this; 3 | 4 | self.isTouchDevice = navigator.userAgent.match(/(iPhone|iPod|iPad|Android|playbook|silk|BlackBerry|BB10|Windows Phone|Tizen|Bada|webOS|IEMobile|Opera Mini)/); 5 | self.isTouch = (('ontouchstart' in window) || (navigator.msMaxTouchPoints > 0) || (navigator.maxTouchPoints)); 6 | 7 | self.error = function(err, title, callback) { 8 | if (err.logout) { 9 | console.log("API:error:::::", err) 10 | window.location.href = '/login?redirect_uri=' + encodeURIComponent(window.location.href); 11 | return 12 | }; 13 | if (err.loginRequired) 14 | return 15 | if (err){ 16 | if (err.error) { 17 | self.alert({title: title || "Error", text:err.error}, callback) 18 | }else if (err.warning) { 19 | self.alert({title: title || "Warning", text:err.warning}, callback) 20 | }else if (err.info) { 21 | self.alert({title: title || "Info", text:err.info}, callback) 22 | } 23 | }; 24 | //err && alert(err.error); 25 | return err; 26 | } 27 | 28 | self.buildAlertDialog = function(args, callback){ 29 | var $dialog = $('.alert-dialog'); 30 | $dialog.find('.title-text').html(args.title); 31 | $dialog.find('.msg-text').html(args.text); 32 | $dialog.find('.mdl-card__menu').hide(); 33 | $dialog.find('.cancel-btn').html(args.cancelBtn || 'CANCEL').hide(); 34 | $dialog.find('.ok-btn').html(args.okBtn || 'OK'); 35 | $dialog[0].callback = callback || false; 36 | if (!$dialog[0].callbackBind) { 37 | $dialog[0].callbackBind = true; 38 | $dialog.find('.ok-btn').on('click', function () { 39 | $dialog.irisDialog(false); 40 | $dialog[0].callback && $dialog[0].callback('ok'); 41 | }); 42 | $dialog.find('.cancel-btn').on('click', function () { 43 | $dialog.irisDialog(false); 44 | $dialog[0].callback && $dialog[0].callback('cancel'); 45 | }) 46 | }; 47 | return $dialog; 48 | } 49 | 50 | self.alert = function (args, callback) { 51 | var $dialog = self.buildAlertDialog(args, callback); 52 | $dialog.irisDialog(true); 53 | } 54 | /* 55 | Api.confirm({title: "Are you sure?", text:"Do you want to delete item", okBtn: "DELETE", cancelBtn: "No"}, function (btn) { 56 | if (btn == 'ok') { deleteItem() }; 57 | }); 58 | */ 59 | self.confirm = function (args, callback) { 60 | var $dialog = self.buildAlertDialog(args, callback); 61 | $dialog.find('.cancel-btn').show(); 62 | $dialog.irisDialog(true); 63 | } 64 | 65 | self.setAlertMsg = function (info, msgHolder){ 66 | var $status = $(msgHolder || '.alert-msg'); 67 | if (info.error) { 68 | $status.find('div').html(info.error); 69 | $status.addClass('error'); 70 | }else{ 71 | $status.find('div').html(info.message); 72 | $status.removeClass('error'); 73 | } 74 | 75 | $status.fadeIn(); 76 | } 77 | 78 | self.post = function(path, data, callback, method) { 79 | $.ajax({ 80 | dataType: "json", 81 | method : method || 'POST', 82 | url: path, 83 | data: data, 84 | error : function(err) { 85 | if(err.responseJSON) { 86 | if (err.responseJSON.error) 87 | callback(err.responseJSON); 88 | else if (err.responseJSON.request) { 89 | callback({ request : err.responseJSON.request }); 90 | } 91 | } else 92 | callback({ error : err.statusText }); 93 | }, 94 | success: function(o) { 95 | callback(null, o); 96 | } 97 | }) 98 | } 99 | 100 | self.get = function(path, data, callback) { 101 | self.post(path, data, callback, "GET") 102 | } 103 | 104 | self.hideLoading = function () { 105 | $('body').removeClass('loading'); 106 | } 107 | 108 | self.showLoading = function () { 109 | $('body').addClass('loading'); 110 | } 111 | 112 | self.handleMsg = function(holderSelector){ 113 | if (!window.App || !window.App.msg) 114 | return; 115 | 116 | var $el = $(holderSelector); 117 | $el.removeClass('error'); 118 | 119 | check(window.App.msg); 120 | 121 | function check(msg){ 122 | $.each(msg, function(key, a){ 123 | console.log(key, a) 124 | if ($.type(a) == "string") { 125 | $el.find('div').html(a); 126 | $el.fadeIn(); 127 | }else if($.type(a) == "array"){ 128 | $.each(a, function(_key, info){ 129 | if ($.type(info.error) == "string") { 130 | if (info.code != "access_denied") {//dont show is user have denied access to login, he know what he did 131 | $el.find('div').html(info.error); 132 | $el.fadeIn(); 133 | }; 134 | }else if (info.error) { 135 | check(info.error) 136 | $el.addClass('error') 137 | }else if(key == 'activation' && info.success){ 138 | $el.find('div').html(a.message || "Activation completed. Now you can Sign-In"); 139 | $el.fadeIn(); 140 | } 141 | }); 142 | }else if($.type(a) == "object"){ 143 | console.log(key+"sssss", a) 144 | if (a.error) { 145 | check(a.error) 146 | $el.addClass('error') 147 | }else if(key == 'activation' && a.success){ 148 | $el.find('div').html(a.message || "Activation completed. Now you can Sign-In"); 149 | $el.fadeIn(); 150 | } 151 | } 152 | }) 153 | } 154 | }; 155 | 156 | self.activatePage = function(index){ 157 | //$('#pages').get(0).selected = index; 158 | var current = $('.app-pages').attr('current'); 159 | if (current==index){ 160 | return; 161 | }; 162 | var cls = { 163 | in: "moveFromRight", 164 | out: "moveToLeft" 165 | } 166 | 167 | if (current < index) { 168 | cls = { 169 | in: "moveFromLeft", 170 | out: "moveToRight" 171 | } 172 | }; 173 | 174 | 175 | PageTransitions.animate({ 176 | block: $('.app-pages').get(0), 177 | page: index, 178 | inClass: cls.in, 179 | outClass: cls.out 180 | }); 181 | }, 182 | self.changeLocale = function(url) { 183 | window.location.href = url+window.location.hash; 184 | } 185 | 186 | } 187 | 188 | var Api = new API(jQuery); 189 | (function($){ 190 | $(document).ready(function(){ 191 | Api.handleMsg('.alert-msg'); 192 | 193 | $('.alert .close').on('click', function(){ 194 | $(this).closest('.alert').fadeOut(); 195 | }); 196 | $('.dialog .close-dialog').on('click', function(){ 197 | $(this).closest('.dialog').irisDialog(false); 198 | }) 199 | 200 | $('.home-page .mdl-layout__content').on('scroll', function(e){ 201 | $(document.body).toggleClass('fixed-header', $(this).scrollTop()+64 > $('#videocontainer').height());//+$('.mdl-layout__header-row').height()) 202 | //console.log("sss", e, ) 203 | }) 204 | 205 | $('.scroll-to').on('click', function(){ 206 | var $to = $($(this).data('el')); 207 | var $scroller = $('.mdl-layout__content'); 208 | $scroller.animate({ 209 | scrollTop: $scroller.scrollTop() + $to.offset().top 210 | }); 211 | }) 212 | //$('.app-pages').height($('.app-pages').parent().height()); 213 | //$(document.body).append($('.modal-dialog-back')); 214 | 215 | $.fn.irisDialog = function(open){ 216 | 217 | if (!$._irisDialogResizer) { 218 | $(window).on('resize', function(){ 219 | $('.iris-js-dialog:visible').each(function () { 220 | $._irisDialogResizer($(this)); 221 | }) 222 | }); 223 | 224 | $._irisDialogResizer = function ($dialog) { 225 | $dialog.css({ 226 | top: ( $(window).height() - $dialog.outerHeight())/2 , 227 | left: ( $(window).width() - $dialog.outerWidth())/2 , 228 | margin: 0 229 | }); 230 | } 231 | }; 232 | 233 | var $dialog = $(this); 234 | 235 | $._irisDialogResizer($dialog); 236 | 237 | if (open === true){ 238 | if ($dialog.hasClass('modal')) { 239 | $._irisDialogModalCount++; 240 | $('.modal-dialog-back').fadeIn(); 241 | }; 242 | $dialog.removeClass('mdl-hidden') 243 | $dialog.fadeIn(); 244 | }else if(open === false){ 245 | $dialog.fadeOut(); 246 | if($._irisDialogModalCount > 0){ 247 | $._irisDialogModalCount--; 248 | } 249 | 250 | if($._irisDialogModalCount <= 0){ 251 | $._irisDialogModalCount = 0; 252 | $('.modal-dialog-back').fadeOut(); 253 | } 254 | } 255 | 256 | } 257 | $._irisDialogModalCount = 0; 258 | }); 259 | })(jQuery); 260 | 261 | 262 | 263 | -------------------------------------------------------------------------------- /http/site/scripts/jquery.easing.1.3.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/ 3 | * 4 | * Uses the built in easing capabilities added In jQuery 1.1 5 | * to offer multiple easing options 6 | * 7 | * TERMS OF USE - jQuery Easing 8 | * 9 | * Open source under the BSD License. 10 | * 11 | * Copyright © 2008 George McGinley Smith 12 | * All rights reserved. 13 | * 14 | * Redistribution and use in source and binary forms, with or without modification, 15 | * are permitted provided that the following conditions are met: 16 | * 17 | * Redistributions of source code must retain the above copyright notice, this list of 18 | * conditions and the following disclaimer. 19 | * Redistributions in binary form must reproduce the above copyright notice, this list 20 | * of conditions and the following disclaimer in the documentation and/or other materials 21 | * provided with the distribution. 22 | * 23 | * Neither the name of the author nor the names of contributors may be used to endorse 24 | * or promote products derived from this software without specific prior written permission. 25 | * 26 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 27 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 28 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 29 | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 30 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 31 | * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 32 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 33 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 34 | * OF THE POSSIBILITY OF SUCH DAMAGE. 35 | * 36 | */ 37 | 38 | // t: current time, b: begInnIng value, c: change In value, d: duration 39 | jQuery.easing['jswing'] = jQuery.easing['swing']; 40 | 41 | jQuery.extend( jQuery.easing, 42 | { 43 | def: 'easeOutQuad', 44 | swing: function (x, t, b, c, d) { 45 | //alert(jQuery.easing.default); 46 | return jQuery.easing[jQuery.easing.def](x, t, b, c, d); 47 | }, 48 | easeInQuad: function (x, t, b, c, d) { 49 | return c*(t/=d)*t + b; 50 | }, 51 | easeOutQuad: function (x, t, b, c, d) { 52 | return -c *(t/=d)*(t-2) + b; 53 | }, 54 | easeInOutQuad: function (x, t, b, c, d) { 55 | if ((t/=d/2) < 1) return c/2*t*t + b; 56 | return -c/2 * ((--t)*(t-2) - 1) + b; 57 | }, 58 | easeInCubic: function (x, t, b, c, d) { 59 | return c*(t/=d)*t*t + b; 60 | }, 61 | easeOutCubic: function (x, t, b, c, d) { 62 | return c*((t=t/d-1)*t*t + 1) + b; 63 | }, 64 | easeInOutCubic: function (x, t, b, c, d) { 65 | if ((t/=d/2) < 1) return c/2*t*t*t + b; 66 | return c/2*((t-=2)*t*t + 2) + b; 67 | }, 68 | easeInQuart: function (x, t, b, c, d) { 69 | return c*(t/=d)*t*t*t + b; 70 | }, 71 | easeOutQuart: function (x, t, b, c, d) { 72 | return -c * ((t=t/d-1)*t*t*t - 1) + b; 73 | }, 74 | easeInOutQuart: function (x, t, b, c, d) { 75 | if ((t/=d/2) < 1) return c/2*t*t*t*t + b; 76 | return -c/2 * ((t-=2)*t*t*t - 2) + b; 77 | }, 78 | easeInQuint: function (x, t, b, c, d) { 79 | return c*(t/=d)*t*t*t*t + b; 80 | }, 81 | easeOutQuint: function (x, t, b, c, d) { 82 | return c*((t=t/d-1)*t*t*t*t + 1) + b; 83 | }, 84 | easeInOutQuint: function (x, t, b, c, d) { 85 | if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b; 86 | return c/2*((t-=2)*t*t*t*t + 2) + b; 87 | }, 88 | easeInSine: function (x, t, b, c, d) { 89 | return -c * Math.cos(t/d * (Math.PI/2)) + c + b; 90 | }, 91 | easeOutSine: function (x, t, b, c, d) { 92 | return c * Math.sin(t/d * (Math.PI/2)) + b; 93 | }, 94 | easeInOutSine: function (x, t, b, c, d) { 95 | return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b; 96 | }, 97 | easeInExpo: function (x, t, b, c, d) { 98 | return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b; 99 | }, 100 | easeOutExpo: function (x, t, b, c, d) { 101 | return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b; 102 | }, 103 | easeInOutExpo: function (x, t, b, c, d) { 104 | if (t==0) return b; 105 | if (t==d) return b+c; 106 | if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b; 107 | return c/2 * (-Math.pow(2, -10 * --t) + 2) + b; 108 | }, 109 | easeInCirc: function (x, t, b, c, d) { 110 | return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b; 111 | }, 112 | easeOutCirc: function (x, t, b, c, d) { 113 | return c * Math.sqrt(1 - (t=t/d-1)*t) + b; 114 | }, 115 | easeInOutCirc: function (x, t, b, c, d) { 116 | if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b; 117 | return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b; 118 | }, 119 | easeInElastic: function (x, t, b, c, d) { 120 | var s=1.70158;var p=0;var a=c; 121 | if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3; 122 | if (a < Math.abs(c)) { a=c; var s=p/4; } 123 | else var s = p/(2*Math.PI) * Math.asin (c/a); 124 | return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; 125 | }, 126 | easeOutElastic: function (x, t, b, c, d) { 127 | var s=1.70158;var p=0;var a=c; 128 | if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3; 129 | if (a < Math.abs(c)) { a=c; var s=p/4; } 130 | else var s = p/(2*Math.PI) * Math.asin (c/a); 131 | return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b; 132 | }, 133 | easeInOutElastic: function (x, t, b, c, d) { 134 | var s=1.70158;var p=0;var a=c; 135 | if (t==0) return b; if ((t/=d/2)==2) return b+c; if (!p) p=d*(.3*1.5); 136 | if (a < Math.abs(c)) { a=c; var s=p/4; } 137 | else var s = p/(2*Math.PI) * Math.asin (c/a); 138 | if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; 139 | return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b; 140 | }, 141 | easeInBack: function (x, t, b, c, d, s) { 142 | if (s == undefined) s = 1.70158; 143 | return c*(t/=d)*t*((s+1)*t - s) + b; 144 | }, 145 | easeOutBack: function (x, t, b, c, d, s) { 146 | if (s == undefined) s = 1.70158; 147 | return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b; 148 | }, 149 | easeInOutBack: function (x, t, b, c, d, s) { 150 | if (s == undefined) s = 1.70158; 151 | if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b; 152 | return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b; 153 | }, 154 | easeInBounce: function (x, t, b, c, d) { 155 | return c - jQuery.easing.easeOutBounce (x, d-t, 0, c, d) + b; 156 | }, 157 | easeOutBounce: function (x, t, b, c, d) { 158 | if ((t/=d) < (1/2.75)) { 159 | return c*(7.5625*t*t) + b; 160 | } else if (t < (2/2.75)) { 161 | return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b; 162 | } else if (t < (2.5/2.75)) { 163 | return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b; 164 | } else { 165 | return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b; 166 | } 167 | }, 168 | easeInOutBounce: function (x, t, b, c, d) { 169 | if (t < d/2) return jQuery.easing.easeInBounce (x, t*2, 0, c, d) * .5 + b; 170 | return jQuery.easing.easeOutBounce (x, t*2-d, 0, c, d) * .5 + c*.5 + b; 171 | } 172 | }); 173 | 174 | /* 175 | * 176 | * TERMS OF USE - EASING EQUATIONS 177 | * 178 | * Open source under the BSD License. 179 | * 180 | * Copyright © 2001 Robert Penner 181 | * All rights reserved. 182 | * 183 | * Redistribution and use in source and binary forms, with or without modification, 184 | * are permitted provided that the following conditions are met: 185 | * 186 | * Redistributions of source code must retain the above copyright notice, this list of 187 | * conditions and the following disclaimer. 188 | * Redistributions in binary form must reproduce the above copyright notice, this list 189 | * of conditions and the following disclaimer in the documentation and/or other materials 190 | * provided with the distribution. 191 | * 192 | * Neither the name of the author nor the names of contributors may be used to endorse 193 | * or promote products derived from this software without specific prior written permission. 194 | * 195 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 196 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 197 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 198 | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 199 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 200 | * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 201 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 202 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 203 | * OF THE POSSIBILITY OF SUCH DAMAGE. 204 | * 205 | */ -------------------------------------------------------------------------------- /tools/release/setup.js: -------------------------------------------------------------------------------- 1 | var _ = require('underscore'); 2 | var fs = require('fs'); 3 | var path = require('path'); 4 | var UUID = require('node-uuid'); 5 | var crypto = require('crypto'); 6 | var rs = require('readline-sync'); 7 | var request = require('request'); 8 | var progress = require('request-progress'); 9 | var irisUtils = require('iris-utils'); 10 | var exec = require('child_process').exec; 11 | var execSync = require('child_process').execSync; 12 | var Sia = require('sia-api'); 13 | 14 | var root = path.join(__dirname,'../../'); 15 | var temp = '/tmp'; 16 | 17 | var platform = process.platform; 18 | if(platform == 'win32') { 19 | platform = 'windows'; 20 | var temp = path.join(process.env.TEMP); 21 | } 22 | 23 | function testFile(file) { 24 | try { 25 | fs.accessSync(file); 26 | return true; 27 | } 28 | catch(ex) { 29 | return false; 30 | } 31 | } 32 | 33 | var force = process.argv.join(' ').match(/--force/ig) ? true : false; 34 | var nomongo = process.argv.join(' ').match(/--nomongo/ig) ? true : false; 35 | 36 | if(!force && testFile(path.join(root,'config/sia-cluster.local.conf'))) { 37 | console.log("\nDid setup run already?".red.bold) 38 | console.log("config/sia-cluster.local.conf".bold+" already exists!".bold) 39 | console.log("\nUse "+"--force".bold+" to re-initialize (you will loose your settings!)\n\n") 40 | process.exit(0); 41 | } 42 | 43 | // --- 44 | /* 45 | Object.defineProperty(Number.prototype, 'toFileSize', { 46 | value: function(a, asNumber){ 47 | var b,c,d; 48 | var r = ( 49 | a=a?[1e3,'k','B']:[1024,'K','iB'], 50 | b=Math, 51 | c=b.log, 52 | d=c(this)/c(a[0])|0,this/b.pow(a[0],d) 53 | ).toFixed(2) 54 | 55 | if(!asNumber){ 56 | r += ' '+(d?(a[1]+'MGTPEZY')[--d]+a[2]:'Bytes'); 57 | } 58 | return r; 59 | }, 60 | writable:false, 61 | enumerable:false 62 | });*/ 63 | 64 | function fetch(options, callback) { 65 | 66 | var MAX = 60, MIN = 0, value = 0; 67 | console.log("Downloading: "+options.file); 68 | progress(request(options.url), { 69 | throttle: 250, // Throttle the progress event to 2000ms, defaults to 1000ms 70 | delay: 1000, // Only start to emit after 1000ms delay, defaults to 0ms 71 | // lengthHeader: 'x-transfer-length' // Length header to use, defaults to content-length 72 | }) 73 | .on('progress', function (state) { 74 | if(state.percentage > 0.99) 75 | state.percentage = 1; 76 | var value = Math.ceil(state.percentage * 60); 77 | console.log('\x1B[1A\x1B[K|' + 78 | (new Array(value + 1)).join('=') + '>' + 79 | (new Array(MAX - value + 1)).join('-') + '| ' + (state.percentage*100).toFixed(1) + '% ' 80 | + state.size.transferred.toFileSize().split(' ').shift()+'/' 81 | + state.size.total.toFileSize()+' ' 82 | + state.speed.toFileSize()+'/s' 83 | ); 84 | }) 85 | .on('error', function (err) { 86 | err && console.log(err.toString()); 87 | callback(err); 88 | }) 89 | .pipe(fs.createWriteStream(options.file)) 90 | .on('finish', function(err) { 91 | console.log(""); 92 | err && console.log(err.toString()); 93 | callback(); 94 | }); 95 | } 96 | 97 | // ------------------------- 98 | 99 | var mongoPath = null; 100 | var mongoRootPath = path.join(process.env.ProgramFiles || '',"MongoDB/Server"); 101 | 102 | function init() { 103 | 104 | console.log(""); 105 | console.log("Creating user for Sia Cluster web login...".bold); 106 | console.log(""); 107 | var username = rs.question("Username:"); 108 | if(!username) { 109 | console.log("You must specify username. Aborting..."); 110 | process.exit(1); 111 | } 112 | var pass = rs.question("Password:", { hideEchoBack : true }); 113 | if(!pass) { 114 | console.log("You must specify password. Aborting..."); 115 | process.exit(1); 116 | } 117 | var passHash = crypto.createHash("sha256").update(pass).digest('hex'); 118 | 119 | // -- 120 | 121 | var local_conf = fs.readFileSync(path.join(root,'config/sia-cluster.local.conf-example'), { encoding : 'utf-8' }); 122 | var auth = crypto.createHash("sha256").update(UUID.v1()+UUID.v4+root.toString()+username+pass).digest('hex'); 123 | console.log("\nYour auth:\n\n"+auth.cyan.bold+"\n(You can find this later in "+"config/sia-cluster.local.conf".bold+")"); 124 | local_conf = local_conf 125 | .replace('1299ece0263565a53df103a34910884d5016a10d86c06e5f309f17761a965d28',auth) 126 | .replace('"test": {pass: "13a5c202e320d0bf9bb2c6e2c7cf380a6f7de5d392509fee260b809c893ff2f9"}', 127 | '"'+username+'": {pass: "'+passHash+'"}'); 128 | // console.log(local_conf) 129 | 130 | fs.writeFileSync(path.join(root,'config/sia-cluster.local.conf'), local_conf); 131 | 132 | // --- 133 | 134 | if(platform == "windows") { 135 | var application = "@echo off\n" 136 | +"cd ..\n" 137 | +(nomongo ? "" : "start /MIN "+mongoPath+"\\bin\\mongod.exe --dbpath "+path.join(root,'/data/db')+" \n") 138 | +"bin\\node\\node sia-cluster %*\n" 139 | +"cd bin\n"; 140 | 141 | var service = "@echo off\n" 142 | +"cd ..\n" 143 | +(nomongo ? "" : "start /MIN "+mongoPath+"\\bin\\mongod.exe --dbpath "+path.join(root,'/data/db')+" \n") 144 | +"bin\\node\\node run sia-cluster %*\n" 145 | +"cd bin\n"; 146 | 147 | fs.writeFileSync(path.join(root,'bin/sia-cluster.bat'), application); 148 | fs.writeFileSync(path.join(root,'bin/sia-cluster-service.bat'), service); 149 | } 150 | else { 151 | var DIR = 'DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )";'; 152 | 153 | var application = "# !/bin/bash\n" 154 | +"pushd . > /dev/null\n" 155 | +DIR+'\n' 156 | +"cd $DIR/..\n" 157 | +"bin/node/node sia-cluster \"$@\"\n" 158 | +"popd > /dev/null\n"; 159 | 160 | var service = "# !/bin/bash\n" 161 | +"pushd . > /dev/null\n" 162 | +DIR+'\n' 163 | +"cd $DIR/..\n" 164 | +"bin/node/node run sia-cluster \"$@\"\n" 165 | +"popd > /dev/null\n"; 166 | 167 | var p = path.join(root,'bin/sia-cluster').toString(); 168 | fs.writeFileSync(p, application); 169 | execSync("chmod a+x "+p) 170 | fs.writeFileSync(p+'-service', service); 171 | execSync("chmod a+x "+p+'-service') 172 | } 173 | 174 | // --- 175 | var suffix = platform == "windows" ? "bat" : ""; 176 | console.log("To run, start one of the following:\n"); 177 | console.log(("bin/sia-cluster."+suffix).bold+" - application"); 178 | console.log(("bin/sia-cluster-service."+suffix).bold+" - service"); 179 | console.log("\nYou can access Web UI at "+"http://localhost:5566\n".yellow.bold); 180 | 181 | var Sia = require('sia-api'); 182 | var sia = new Sia({ 183 | host : "http://127.0.0.1:9980", 184 | timeout : 3 * 1000, 185 | verbose : false 186 | }); 187 | console.log("Checking for local Sia daemon...") 188 | sia.daemon.version(function(err, resp) { 189 | if(err) { 190 | console.log(""); 191 | console.log("Warning: Unable to connect to local Sia daemon".magenta.bold); 192 | console.log(err.toString()); 193 | console.log("Please start and sync Sia before running Sia Cluster".yellow.bold); 194 | } 195 | else 196 | console.log("Found local Sia daemon version:".cyan.bold, resp.version.bold); 197 | }) 198 | 199 | 200 | } 201 | 202 | function getMongoPath() { 203 | try { 204 | var list = fs.readdirSync(mongoRootPath,{ encoding : 'utf-8' }); 205 | list = list.sort(function(a,b) { return parseFloat(b)-parseFloat(a); }) 206 | list = _.filter(list, function(v) { 207 | if(!parseFloat(v)) 208 | return false; 209 | var s = fs.statSync(path.join(mongoRootPath,v)); 210 | if(s.isDirectory()) 211 | return true; 212 | return false; 213 | }) 214 | var mongo = list.shift(); 215 | if(!mongo) 216 | return null; 217 | return path.join(mongoRootPath,mongo); 218 | } 219 | catch(ex) { 220 | console.log(ex.toString); 221 | return null; 222 | } 223 | } 224 | 225 | 226 | function installMongoDb(callback) { 227 | var mongodbFile = path.join(temp,"mongodb-win32-x86_64-2008plus-ssl-latest-signed.msi"); 228 | fetch({ url : "http://downloads.mongodb.org/win32/mongodb-win32-x86_64-2008plus-ssl-latest-signed.msi", 229 | file : mongodbFile, 230 | // size : 154128896 231 | }, 232 | function() { 233 | console.log("Waiting for MongoDB install...") 234 | var e = 'msiexec /i '+mongodbFile; 235 | // console.log(e); 236 | exec(e, function(err) { 237 | // console.log(err) 238 | callback(); 239 | 240 | }); 241 | }) 242 | } 243 | 244 | 245 | function main() { 246 | console.log(''); 247 | 248 | if(platform == 'windows') { 249 | var mongoPath = getMongoPath(); 250 | console.log("MongoDB Found at:",mongoPath); 251 | if(!nomongo && !mongoPath) { 252 | console.log("MongoDB not found!".yellow.bold); 253 | if (rs.keyInYN('Do you want to install MongoDb?')) { 254 | console.log(''); 255 | installMongoDb(function() { 256 | init(); // --> 257 | }); 258 | return; 259 | } 260 | } 261 | } 262 | init(); // --> 263 | } 264 | 265 | main(); 266 | -------------------------------------------------------------------------------- /http/app/themes/sia-cluster.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /http/app/sia-cluster.html: -------------------------------------------------------------------------------- 1 | 2 | 177 | 322 | -------------------------------------------------------------------------------- /lib/price-aggregator.js: -------------------------------------------------------------------------------- 1 | var _ = require("iris-underscore"); 2 | 3 | function TO_GB(n) { return n * 1024; } 4 | function TO_TB(n) { return n * 1024 * 1024; } 5 | 6 | function PriceAggregator(core, nexus, markets, sia, config) { 7 | var self = this; 8 | self.markets = markets; 9 | self.sia = sia; 10 | self.ts = 0; 11 | self.sinks = [ ] 12 | self.pricing = { 13 | TARGET_USD_TB_MO : config.TARGET_USD_TB_MO || 5, 14 | TRACK_STORAGE_PRICE : (config.TRACK_STORAGE_PRICE === undefined ? true : config.TRACK_STORAGE_PRICE), 15 | TRACK_MODE : (config.TRACK_MODE === undefined ? "PEG" : config.TRACK_MODE), 16 | AVG_PRICE_FACTOR : config.AVG_PRICE_FACTOR || 1.0, 17 | MIN_PRICE : config.MIN_PRICE || 100, 18 | MAX_PRICE : config.MAX_PRICE || 20000, 19 | PRICE_UPDATE_FREQ : config.PRICE_UPDATE_FREQ || 5 20 | } 21 | 22 | self.hostStorage = { 23 | name : "root", 24 | children : [], 25 | total: 0 26 | } 27 | 28 | self.data = { } 29 | _.each(self.markets.exchanges, function(exchange, ident) { 30 | self.data[ident] = exchange.ctx; 31 | }) 32 | 33 | self.init = function(callback) { 34 | self.updateLoop(callback); 35 | } 36 | 37 | self.registerSink = function(fn) { 38 | self.sinks.push(fn); 39 | } 40 | 41 | function updateLoop(callback) { 42 | return self.updateLoop(callback) 43 | } 44 | 45 | self.updateLoop = function(callback) { 46 | 47 | var ts = Date.now(); 48 | if(ts-self.ts < (self.pricing.PRICE_UPDATE_FREQ*60*1000)) { 49 | return dpc(1000, updateLoop) 50 | } 51 | 52 | self.markets.update(function() { 53 | 54 | //_.each(self.data, function(o, ident) { 55 | // console.log(ident.toUpperCase(), o.tickers); 56 | //}) 57 | //console.log(self.data.kraken.tickers); 58 | 59 | 60 | self.updatePricing(function(err) { 61 | if(err) 62 | console.log(err); 63 | 64 | callback && dpc(function() { callback(null, self.pricing); }); 65 | 66 | if(err) { 67 | dpc(1000, updateLoop); 68 | } 69 | else 70 | self.flushToSinks(function() { 71 | dpc(1000, updateLoop); 72 | }) 73 | }); 74 | }) 75 | } 76 | 77 | self.flushToSinks = function(callback) { 78 | _.asyncMap(self.sinks, function(fn, callback) { 79 | fn(self.pricing, function() { 80 | callback(); 81 | }); 82 | }, function() { 83 | callback(); 84 | }) 85 | } 86 | 87 | self.UPDATE = function(n, v) { 88 | if(!_.contains(_.keys(self.pricing), n)) { 89 | console.log("priceAggregator Error: No such configuration parameter:".magenta.bold, (n+'').bold) 90 | return; 91 | } 92 | 93 | if(n == "PRICE_UPDATE_FREQ" && v < 1) 94 | v = 1; 95 | 96 | self.pricing[n] = v; 97 | 98 | self.updatePricing(function(err) { 99 | if(err) 100 | console.log(err); 101 | else 102 | self.flushToSinks(_.noop); 103 | }); 104 | } 105 | 106 | self.SET_TARGET_USD_TB_MO = function(T) { 107 | console.log("priceAggregator::SET_TARGET_USD_TB_MO is DEPRECATED!".magenta.bold); 108 | self.pricing.TARGET_USD_TB_MO = T; 109 | self.updatePricing(function(err) { 110 | if(err) 111 | console.log(err); 112 | else 113 | self.flushToSinks(_.noop); 114 | }); 115 | } 116 | 117 | self.USD_TB_to_SC_GB = function(T) { 118 | return Math.round(T / self.pricing.SC_USD / 1000); 119 | } 120 | 121 | self.USD_TB_to_SC_TB = function(T) { 122 | return Math.round(T / self.pricing.SC_USD); 123 | } 124 | 125 | self.updatePricing = function(callback) { 126 | 127 | self.ts = Date.now(); 128 | 129 | var BTC_USD_list = [ ] 130 | 131 | _.each(self.data, function(o, ident) { 132 | //console.log(ident.toUpperCase(), o.tickers); 133 | if(o.tickers && o.tickers['BTC/USD'] && o.tickers['BTC/USD'].ask) 134 | BTC_USD_list.push(o.tickers['BTC/USD'].ask); 135 | }) 136 | 137 | var SC_CNY_list = [ ] 138 | _.each(self.data, function(o, ident) { 139 | //console.log(ident.toUpperCase(), o.tickers); 140 | if(o.tickers && o.tickers['SC/CNY'] && o.tickers['SC/CNY'].ask) 141 | SC_CNY_list.push(o.tickers['SC/CNY'].ask); 142 | }) 143 | 144 | 145 | if(!BTC_USD_list.length) 146 | return callback("Unable to fetch BTC rate from exchanges, aborting price aggregation..."); 147 | 148 | self.pricing.BTC_USD = _.reduce(BTC_USD_list, function(m,n) { return m+n; }) / BTC_USD_list.length; 149 | // console.log("BTC_USD",self.pricing.BTC_USD," ---> ",BTC_USD_list); 150 | 151 | self.pricing.SC_CNY = _.reduce(SC_CNY_list, function(m,n) { return m+n; }) / SC_CNY_list.length; 152 | // console.log("SC_CNY",self.pricing.SC_CNY," ---> ",SC_CNY_list); 153 | 154 | self.pricing.SC_BTC = self.data.polo.tickers['SC/BTC'].ask; 155 | // console.log("SC_BTC", self.pricing.SC_BTC); 156 | 157 | var SC_USD_list = [ ] 158 | SC_USD_list.push(self.pricing.SC_BTC * self.pricing.BTC_USD); 159 | 160 | var CNY_USD = self.data.ecb.tickers["CNY/USD"]; 161 | CNY_USD && _.each(SC_CNY_list, function(v) { 162 | SC_USD_list.push(v / CNY_USD); 163 | }) 164 | 165 | self.pricing.SC_USD = _.reduce(SC_USD_list, function(m,n) { return m+n; }) / SC_USD_list.length; 166 | // console.log("SC_USD",self.pricing.SC_USD," ---> ",SC_USD_list); 167 | 168 | self.pricing.SC_TB_MO = self.USD_TB_to_SC_TB(self.pricing.TARGET_USD_TB_MO); 169 | // console.log("TARGET",self.pricing.TARGET_USD_TB_MO,"SC_TB_MO",self.pricing.SC_TB_MO); 170 | // console.log(self.pricing); 171 | 172 | // _.each(self.pricing, function(v,n) { 173 | // console.log(n.bold+': '+(v+'').bold); 174 | // }) 175 | 176 | self.updateHostAverages(function(err) { 177 | 178 | var SC_TB_MO = self.pricing.SC_TB_MO; 179 | if(!err)// && !self.pricing.MARKET_USD_PEG) 180 | { 181 | switch(self.pricing.TRACK_MODE) { 182 | case "AVG": { 183 | SC_TB_MO = self.pricing.AVG_SC_TB_MO * self.pricing.AVG_PRICE_FACTOR; 184 | } break; 185 | case "WAVG": { 186 | SC_TB_MO = self.pricing.W_AVG_SC_TB_MO * self.pricing.AVG_PRICE_FACTOR; 187 | } break; 188 | default: { 189 | // PEG 190 | } break; 191 | } 192 | } 193 | 194 | if(SC_TB_MO < self.pricing.MIN_PRICE) 195 | SC_TB_MO = self.pricing.MIN_PRICE; 196 | else 197 | if(SC_TB_MO > self.pricing.MAX_PRICE) 198 | SC_TB_MO = self.pricing.MAX_PRICE; 199 | 200 | self.pricing.SC_TB_MO = Math.round(SC_TB_MO); 201 | 202 | core.verbose && console.log("Cluster SC/TB/MO: ".yellow.bold, self.pricing.SC_TB_MO) 203 | 204 | if(!err) 205 | self.pricing.AVG_VS_CLUSTER_PRICE_DIFF = (Math.round(self.pricing.SC_TB_MO / self.pricing.AVG_SC_TB_MO * 100)-100); 206 | else 207 | self.pricing.AVG_VS_CLUSTER_PRICE_DIFF = 'N/A'; 208 | core.verbose && console.log("Price Difference (AVG vs CLUSTER):".cyan.bold, (self.pricing.AVG_VS_CLUSTER_PRICE_DIFF+'%').bold); 209 | 210 | core.verbose && _.each(self.pricing, function(v,n) { 211 | console.log(n.bold+': '+(v+'').bold); 212 | }) 213 | 214 | callback(); 215 | }) 216 | } 217 | 218 | self.updateHostAverages = function(callback) { 219 | 220 | self.hostStorage = { 221 | name : "root", 222 | children : [], 223 | total : 0 224 | } 225 | 226 | self.sia.hostdb.all(function(err, resp) { 227 | if(!err && resp.hosts) { 228 | self.pricing.PEERS_TOTAL = resp.hosts.length; 229 | } 230 | 231 | self.sia.hostdb.active(function(err, resp) { 232 | if(err || !resp.hosts) { 233 | 234 | self.pricing.AVG_SC_TB_MO = 'N/A'; 235 | self.pricing.AVG_USD_TB_MO = 'N/A'; 236 | self.pricing.W_AVG_SC_TB_MO = 'N/A'; 237 | self.pricing.W_AVG_USD_TB_MO = 'N/A'; 238 | self.pricing.PEERS_TOTAL = 'N/A'; 239 | self.pricing.PEERS_ACTIVE = 'N/A'; 240 | self.pricing.PEERS_HOSTING = 'N/A'; 241 | 242 | if(err) { 243 | console.log("Error averaging host price:".red.bold, err.toString()); 244 | console.log("Is "+"siad".bold+" running?"); 245 | return callback(new Error("Error averaging host price")); 246 | } 247 | else 248 | if(!resp.hosts) { 249 | console.log("Error averaging host price: No hosts found on the network".red.bold); 250 | return callback(new Error("Error averaging host price")); 251 | } 252 | } 253 | else { 254 | 255 | // get our local nodes 256 | var local = _.map(nexus.nodes,function(node) { 257 | return node.unlockhash; 258 | }) 259 | 260 | // filter out duplicate nodes 261 | var hostList = _.uniq(resp.hosts, false, 'unlockhash'); 262 | 263 | // mark our own nodes, we do not want our price settings 264 | // to influence our own averages etc. 265 | _.each(hostList, function(host) { 266 | host.local = _.contains(local, host.unlockhash); 267 | }) 268 | 269 | // mark nodes that have set extreme pricing 270 | markOutliers(hostList); 271 | 272 | // sort by price 273 | hostList = _.sortBy(hostList, 'p_'); 274 | 275 | 276 | var storageprice = 0; 277 | var weighted = 0; 278 | var totalstorage = 0; 279 | var hosts = 0; 280 | 281 | _.each(hostList, function(host) { 282 | // exclude non-accepting nodes, outliers and our own nodes 283 | if(!host.acceptingcontracts || host.outlier) { 284 | return; 285 | } 286 | 287 | var storageprice_ = parseFloat(host.storageprice); 288 | var totalstorage_ = parseFloat(host.totalstorage); 289 | var remainingstorage_ = parseFloat(host.remainingstorage); 290 | 291 | self.hostStorage.children.push({ 292 | name : host.netaddress, 293 | local : host.local, 294 | price : storageprice_ * 1e12 * 4320, 295 | size : totalstorage_, 296 | children : [{ 297 | name : "Used", 298 | size : totalstorage_ - remainingstorage_ 299 | }, { 300 | name : "Free", 301 | size : remainingstorage_ 302 | }] 303 | }) 304 | 305 | // do not add our own hosts to totals 306 | if(host.local) 307 | return; 308 | 309 | hosts++; 310 | storageprice += storageprice_; 311 | totalstorage += totalstorage_; 312 | weighted += storageprice_ * totalstorage_; 313 | }) 314 | 315 | self.pricing.PEERS_ACTIVE = hostList.length; 316 | self.pricing.PEERS_HOSTING = hosts; 317 | 318 | self.hostStorage.total = totalstorage; 319 | storageprice = storageprice / hosts / 1e24 * 1e12 * (6*24*30);// / 1e24; 320 | 321 | self.pricing.AVG_SC_TB_MO = Math.round(storageprice); 322 | self.pricing.AVG_USD_TB_MO = storageprice * self.pricing.SC_USD; 323 | core.verbose && console.log("Average Network SC/TB/MO:".yellow.bold,(self.pricing.AVG_SC_TB_MO+' SC').bold,(self.pricing.AVG_USD_TB_MO+' USD').bold); 324 | 325 | weighted = weighted / totalstorage / 1e24 * 1e12 * (6*24*30); 326 | self.pricing.W_AVG_SC_TB_MO = Math.round(weighted); 327 | self.pricing.W_AVG_USD_TB_MO = weighted * self.pricing.SC_USD; 328 | core.verbose && console.log("Weighted Average Network SC/TB/MO:".yellow.bold,(self.pricing.W_AVG_SC_TB_MO+' SC').bold,(self.pricing.W_AVG_USD_TB_MO+' USD').bold); 329 | } 330 | 331 | callback(); 332 | }) 333 | }) 334 | } 335 | 336 | function markOutliers(hosts) { 337 | 338 | var sum = 0; 339 | var sumsq = 0; 340 | _.each(hosts, function(host) { 341 | var p = parseFloat(host.storageprice); 342 | host.p_ = p; 343 | sum += p; 344 | sumsq += p*p; 345 | }) 346 | var l = hosts.length; 347 | var mean = sum/l; 348 | var variance = sumsq/l-mean*mean; 349 | var sd = Math.sqrt(variance); 350 | var deviations = 3; 351 | // var outliers = [ ] 352 | _.each(hosts, function(host) { 353 | if(host.p_ < mean - deviations * sd || host.p_ > mean + deviations * sd) 354 | host.outlier = true; 355 | // outliers.push(host); 356 | }) 357 | } 358 | 359 | } 360 | 361 | module.exports = PriceAggregator; -------------------------------------------------------------------------------- /http/app/sia.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 179 | 334 | 335 | --------------------------------------------------------------------------------