app.js 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. var syncthing = angular.module('syncthing', []);
  2. syncthing.controller('SyncthingCtrl', function ($scope, $http) {
  3. var prevDate = 0;
  4. var modelGetOK = true;
  5. $scope.connections = {};
  6. $scope.config = {};
  7. $scope.myID = "";
  8. $scope.nodes = [];
  9. // Strings before bools look better
  10. $scope.settings = [
  11. {id: 'ListenAddress', descr:"Sync Protocol Listen Address", type: 'string', restart: true},
  12. {id: 'GUIAddress', descr: "GUI Listen Address", type: 'string', restart: true},
  13. {id: 'MaxSendKbps', descr: "Outgoing Rate Limit (KBps)", type: 'string', restart: true},
  14. {id: 'RescanIntervalS', descr: "Rescan Interval (s)", type: 'string', restart: true},
  15. {id: 'ReconnectIntervalS', descr: "Reconnect Interval (s)", type: 'string', restart: true},
  16. {id: 'ParallelRequests', descr: "Max Outstanding Requests", type: 'string', restart: true},
  17. {id: 'MaxChangeKbps', descr: "Max File Change Rate (KBps)", type: 'string', restart: true},
  18. {id: 'ReadOnly', descr: "Read Only", type: 'bool', restart: true},
  19. {id: 'AllowDelete', descr: "Allow Delete", type: 'bool', restart: true},
  20. {id: 'FollowSymlinks', descr: "Follow Symlinks", type: 'bool', restart: true},
  21. {id: 'GlobalAnnEnabled', descr: "Global Announce", type: 'bool', restart: true},
  22. {id: 'LocalAnnEnabled', descr: "Local Announce", type: 'bool', restart: true},
  23. ];
  24. function modelGetSucceeded() {
  25. if (!modelGetOK) {
  26. $('#networkError').modal('hide');
  27. modelGetOK = true;
  28. }
  29. }
  30. function modelGetFailed() {
  31. if (modelGetOK) {
  32. $('#networkError').modal({backdrop: 'static', keyboard: false});
  33. modelGetOK = false;
  34. }
  35. }
  36. $http.get("/rest/version").success(function (data) {
  37. $scope.version = data;
  38. });
  39. $http.get("/rest/system").success(function (data) {
  40. $scope.system = data;
  41. $scope.myID = data.myID;
  42. $http.get("/rest/config").success(function (data) {
  43. $scope.config = data;
  44. var nodes = $scope.config.Repositories[0].Nodes;
  45. nodes = nodes.filter(function (x) { return x.NodeID != $scope.myID; });
  46. nodes.sort(function (a, b) {
  47. if (a.NodeID < b.NodeID)
  48. return -1;
  49. return a.NodeID > b.NodeID;
  50. })
  51. $scope.nodes = nodes;
  52. });
  53. });
  54. $scope.refresh = function () {
  55. $http.get("/rest/system").success(function (data) {
  56. $scope.system = data;
  57. });
  58. $http.get("/rest/model").success(function (data) {
  59. $scope.model = data;
  60. modelGetSucceeded();
  61. }).error(function () {
  62. modelGetFailed();
  63. });
  64. $http.get("/rest/connections").success(function (data) {
  65. var now = Date.now();
  66. var td = (now - prevDate) / 1000;
  67. prevDate = now;
  68. $scope.inbps = 0
  69. $scope.outbps = 0
  70. for (var id in data) {
  71. try {
  72. data[id].inbps = Math.max(0, 8 * (data[id].InBytesTotal - $scope.connections[id].InBytesTotal) / td);
  73. data[id].outbps = Math.max(0, 8 * (data[id].OutBytesTotal - $scope.connections[id].OutBytesTotal) / td);
  74. } catch (e) {
  75. data[id].inbps = 0;
  76. data[id].outbps = 0;
  77. }
  78. $scope.inbps += data[id].outbps;
  79. $scope.outbps += data[id].inbps;
  80. }
  81. $scope.connections = data;
  82. });
  83. $http.get("/rest/need").success(function (data) {
  84. var i, name;
  85. for (i = 0; i < data.length; i++) {
  86. name = data[i].Name.split("/");
  87. data[i].ShortName = name[name.length-1];
  88. }
  89. data.sort(function (a, b) {
  90. if (a.ShortName < b.ShortName) {
  91. return -1;
  92. }
  93. if (a.ShortName > b.ShortName) {
  94. return 1;
  95. }
  96. return 0;
  97. });
  98. $scope.need = data;
  99. });
  100. };
  101. $scope.nodeIcon = function (nodeCfg) {
  102. if ($scope.connections[nodeCfg.NodeID]) {
  103. return "ok";
  104. }
  105. return "minus";
  106. };
  107. $scope.nodeStatus = function (nodeCfg) {
  108. if ($scope.connections[nodeCfg.NodeID]) {
  109. return "Connected";
  110. }
  111. return "Disconnected";
  112. };
  113. $scope.nodeIcon = function (nodeCfg) {
  114. if ($scope.connections[nodeCfg.NodeID]) {
  115. return "ok";
  116. }
  117. return "minus";
  118. };
  119. $scope.nodeClass = function (nodeCfg) {
  120. var conn = $scope.connections[nodeCfg.NodeID];
  121. if (conn) {
  122. return "success";
  123. }
  124. return "info";
  125. };
  126. $scope.nodeAddr = function (nodeCfg) {
  127. var conn = $scope.connections[nodeCfg.NodeID];
  128. if (conn) {
  129. return conn.Address;
  130. }
  131. return nodeCfg.Addresses.join(", ");
  132. };
  133. $scope.nodeVer = function (nodeCfg) {
  134. if (nodeCfg.NodeID === $scope.myID) {
  135. return $scope.version;
  136. }
  137. var conn = $scope.connections[nodeCfg.NodeID];
  138. if (conn) {
  139. return conn.ClientVersion;
  140. }
  141. return "";
  142. };
  143. $scope.nodeName = function (nodeCfg) {
  144. if (nodeCfg.Name) {
  145. return nodeCfg.Name;
  146. }
  147. return nodeCfg.NodeID.substr(0, 6);
  148. };
  149. $scope.saveSettings = function () {
  150. $http.post('/rest/config', JSON.stringify($scope.config), {headers: {'Content-Type': 'application/json'}});
  151. $('#settingsTable').collapse('hide');
  152. };
  153. $scope.editNode = function (nodeCfg) {
  154. $scope.currentNode = nodeCfg;
  155. $scope.editingExisting = true;
  156. $scope.currentNode.AddressesStr = nodeCfg.Addresses.join(", ")
  157. $('#editNode').modal({backdrop: 'static', keyboard: false});
  158. };
  159. $scope.addNode = function () {
  160. $scope.currentNode = {NodeID: "", AddressesStr: "dynamic"};
  161. $scope.editingExisting = false;
  162. $('#editNode').modal({backdrop: 'static', keyboard: false});
  163. };
  164. $scope.deleteNode = function () {
  165. $('#editNode').modal('hide');
  166. if (!$scope.editingExisting)
  167. return;
  168. var newNodes = [];
  169. for (var i = 0; i < $scope.nodes.length; i++) {
  170. if ($scope.nodes[i].NodeID !== $scope.currentNode.NodeID) {
  171. newNodes.push($scope.nodes[i]);
  172. }
  173. }
  174. $scope.nodes = newNodes;
  175. $scope.config.Repositories[0].Nodes = newNodes;
  176. $http.post('/rest/config', JSON.stringify($scope.config), {headers: {'Content-Type': 'application/json'}})
  177. }
  178. $scope.saveNode = function () {
  179. $('#editNode').modal('hide');
  180. nodeCfg = $scope.currentNode;
  181. nodeCfg.Addresses = nodeCfg.AddressesStr.split(',').map(function (x) { return x.trim(); });
  182. var done = false;
  183. for (var i = 0; i < $scope.nodes.length; i++) {
  184. if ($scope.nodes[i].NodeID === nodeCfg.NodeID) {
  185. $scope.nodes[i] = nodeCfg;
  186. done = true;
  187. break;
  188. }
  189. }
  190. if (!done) {
  191. $scope.nodes.push(nodeCfg);
  192. }
  193. $scope.nodes.sort(function (a, b) {
  194. if (a.NodeID < b.NodeID)
  195. return -1;
  196. return a.NodeID > b.NodeID;
  197. })
  198. $scope.config.Repositories[0].Nodes = $scope.nodes;
  199. $http.post('/rest/config', JSON.stringify($scope.config), {headers: {'Content-Type': 'application/json'}})
  200. };
  201. $scope.refresh();
  202. setInterval($scope.refresh, 10000);
  203. });
  204. function decimals(val, num) {
  205. if (val === 0) { return 0; }
  206. var digits = Math.floor(Math.log(Math.abs(val))/Math.log(10));
  207. var decimals = Math.max(0, num - digits);
  208. return decimals;
  209. }
  210. syncthing.filter('natural', function() {
  211. return function(input, valid) {
  212. return input.toFixed(decimals(input, valid));
  213. }
  214. });
  215. syncthing.filter('binary', function() {
  216. return function(input) {
  217. if (input === undefined) {
  218. return '0 '
  219. }
  220. if (input > 1024 * 1024 * 1024) {
  221. input /= 1024 * 1024 * 1024;
  222. return input.toFixed(decimals(input, 2)) + ' Gi';
  223. }
  224. if (input > 1024 * 1024) {
  225. input /= 1024 * 1024;
  226. return input.toFixed(decimals(input, 2)) + ' Mi';
  227. }
  228. if (input > 1024) {
  229. input /= 1024;
  230. return input.toFixed(decimals(input, 2)) + ' Ki';
  231. }
  232. return Math.round(input) + ' ';
  233. }
  234. });
  235. syncthing.filter('metric', function() {
  236. return function(input) {
  237. if (input === undefined) {
  238. return '0 '
  239. }
  240. if (input > 1000 * 1000 * 1000) {
  241. input /= 1000 * 1000 * 1000;
  242. return input.toFixed(decimals(input, 2)) + ' G';
  243. }
  244. if (input > 1000 * 1000) {
  245. input /= 1000 * 1000;
  246. return input.toFixed(decimals(input, 2)) + ' M';
  247. }
  248. if (input > 1000) {
  249. input /= 1000;
  250. return input.toFixed(decimals(input, 2)) + ' k';
  251. }
  252. return Math.round(input) + ' ';
  253. }
  254. });
  255. syncthing.filter('short', function() {
  256. return function(input) {
  257. return input.substr(0, 6);
  258. }
  259. });
  260. syncthing.filter('alwaysNumber', function() {
  261. return function(input) {
  262. if (input === undefined) {
  263. return 0;
  264. }
  265. return input;
  266. }
  267. });
  268. syncthing.directive('optionEditor', function() {
  269. return {
  270. restrict: 'C',
  271. replace: true,
  272. transclude: true,
  273. scope: {
  274. setting: '=setting',
  275. },
  276. template: '<input type="text" ng-model="config.Options[setting.id]"></input>',
  277. };
  278. })