mirror of
https://github.com/octoleo/syncthing.git
synced 2025-02-02 03:48:26 +00:00
parent
c42a6b511c
commit
433a0cb9cc
File diff suppressed because one or more lines are too long
3
build.sh
3
build.sh
@ -62,7 +62,8 @@ deps() {
|
|||||||
|
|
||||||
case "$1" in
|
case "$1" in
|
||||||
"")
|
"")
|
||||||
build
|
shift
|
||||||
|
build $*
|
||||||
;;
|
;;
|
||||||
|
|
||||||
race)
|
race)
|
||||||
|
260
gui/app.js
260
gui/app.js
@ -7,7 +7,7 @@ var syncthing = angular.module('syncthing', []);
|
|||||||
|
|
||||||
syncthing.controller('SyncthingCtrl', function ($scope, $http) {
|
syncthing.controller('SyncthingCtrl', function ($scope, $http) {
|
||||||
var prevDate = 0,
|
var prevDate = 0,
|
||||||
modelGetOK = true;
|
getOK = true;
|
||||||
|
|
||||||
$scope.connections = {};
|
$scope.connections = {};
|
||||||
$scope.config = {};
|
$scope.config = {};
|
||||||
@ -16,35 +16,40 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http) {
|
|||||||
$scope.configInSync = true;
|
$scope.configInSync = true;
|
||||||
$scope.errors = [];
|
$scope.errors = [];
|
||||||
$scope.seenError = '';
|
$scope.seenError = '';
|
||||||
|
$scope.model = {};
|
||||||
|
$scope.repos = [];
|
||||||
|
|
||||||
// Strings before bools look better
|
// Strings before bools look better
|
||||||
$scope.settings = [
|
$scope.settings = [
|
||||||
{id: 'ListenStr', descr: 'Sync Protocol Listen Addresses', type: 'text', restart: true},
|
{id: 'ListenStr', descr: 'Sync Protocol Listen Addresses', type: 'text', restart: true},
|
||||||
{id: 'GUIAddress', descr: 'GUI Listen Address', type: 'text', restart: true},
|
{id: 'MaxSendKbps', descr: 'Outgoing Rate Limit (KBps)', type: 'number', restart: true},
|
||||||
{id: 'MaxSendKbps', descr: 'Outgoing Rate Limit (KBps)', type: 'number', restart: true},
|
{id: 'RescanIntervalS', descr: 'Rescan Interval (s)', type: 'number', restart: true},
|
||||||
{id: 'RescanIntervalS', descr: 'Rescan Interval (s)', type: 'number', restart: true},
|
{id: 'ReconnectIntervalS', descr: 'Reconnect Interval (s)', type: 'number', restart: true},
|
||||||
{id: 'ReconnectIntervalS', descr: 'Reconnect Interval (s)', type: 'number', restart: true},
|
{id: 'ParallelRequests', descr: 'Max Outstanding Requests', type: 'number', restart: true},
|
||||||
{id: 'ParallelRequests', descr: 'Max Outstanding Requests', type: 'number', restart: true},
|
{id: 'MaxChangeKbps', descr: 'Max File Change Rate (KBps)', type: 'number', restart: true},
|
||||||
{id: 'MaxChangeKbps', descr: 'Max File Change Rate (KBps)', type: 'number', restart: true},
|
|
||||||
|
|
||||||
{id: 'ReadOnly', descr: 'Read Only', type: 'bool', restart: true},
|
{id: 'GlobalAnnEnabled', descr: 'Global Announce', type: 'bool', restart: true},
|
||||||
{id: 'FollowSymlinks', descr: 'Follow Symlinks', type: 'bool', restart: true},
|
{id: 'LocalAnnEnabled', descr: 'Local Announce', type: 'bool', restart: true},
|
||||||
{id: 'GlobalAnnEnabled', descr: 'Global Announce', type: 'bool', restart: true},
|
{id: 'StartBrowser', descr: 'Start Browser', type: 'bool'},
|
||||||
{id: 'LocalAnnEnabled', descr: 'Local Announce', type: 'bool', restart: true},
|
|
||||||
{id: 'StartBrowser', descr: 'Start Browser', type: 'bool'},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
function modelGetSucceeded() {
|
$scope.guiSettings = [
|
||||||
if (!modelGetOK) {
|
{id: 'Address', descr: 'GUI Listen Addresses', type: 'text', restart: true},
|
||||||
|
{id: 'User', descr: 'GUI Authentication User', type: 'text', restart: true},
|
||||||
|
{id: 'Password', descr: 'GUI Authentication Password', type: 'password', restart: true},
|
||||||
|
];
|
||||||
|
|
||||||
|
function getSucceeded() {
|
||||||
|
if (!getOK) {
|
||||||
$('#networkError').modal('hide');
|
$('#networkError').modal('hide');
|
||||||
modelGetOK = true;
|
getOK = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function modelGetFailed() {
|
function getFailed() {
|
||||||
if (modelGetOK) {
|
if (getOK) {
|
||||||
$('#networkError').modal({backdrop: 'static', keyboard: false});
|
$('#networkError').modal({backdrop: 'static', keyboard: false});
|
||||||
modelGetOK = false;
|
getOK = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,40 +66,22 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http) {
|
|||||||
return a.NodeID > b.NodeID;
|
return a.NodeID > b.NodeID;
|
||||||
}
|
}
|
||||||
|
|
||||||
$http.get('/rest/version').success(function (data) {
|
|
||||||
$scope.version = data;
|
|
||||||
});
|
|
||||||
$http.get('/rest/system').success(function (data) {
|
|
||||||
$scope.system = data;
|
|
||||||
$scope.myID = data.myID;
|
|
||||||
|
|
||||||
$http.get('/rest/config').success(function (data) {
|
|
||||||
$scope.config = data;
|
|
||||||
$scope.config.Options.ListenStr = $scope.config.Options.ListenAddress.join(', ');
|
|
||||||
|
|
||||||
var nodes = $scope.config.Repositories[0].Nodes;
|
|
||||||
nodes.sort(nodeCompare);
|
|
||||||
$scope.nodes = nodes;
|
|
||||||
});
|
|
||||||
$http.get('/rest/config/sync').success(function (data) {
|
|
||||||
$scope.configInSync = data.configInSync;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
$scope.refresh = function () {
|
$scope.refresh = function () {
|
||||||
$http.get('/rest/system').success(function (data) {
|
$http.get('/rest/system').success(function (data) {
|
||||||
|
getSucceeded();
|
||||||
$scope.system = data;
|
$scope.system = data;
|
||||||
});
|
|
||||||
$http.get('/rest/model').success(function (data) {
|
|
||||||
$scope.model = data;
|
|
||||||
modelGetSucceeded();
|
|
||||||
}).error(function () {
|
}).error(function () {
|
||||||
modelGetFailed();
|
getFailed();
|
||||||
|
});
|
||||||
|
$scope.repos.forEach(function (repo) {
|
||||||
|
$http.get('/rest/model/' + repo.ID).success(function (data) {
|
||||||
|
$scope.model[repo.ID] = data;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
$http.get('/rest/connections').success(function (data) {
|
$http.get('/rest/connections').success(function (data) {
|
||||||
var now = Date.now(),
|
var now = Date.now(),
|
||||||
td = (now - prevDate) / 1000,
|
td = (now - prevDate) / 1000,
|
||||||
id;
|
id;
|
||||||
|
|
||||||
prevDate = now;
|
prevDate = now;
|
||||||
$scope.inbps = 0;
|
$scope.inbps = 0;
|
||||||
@ -116,28 +103,23 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http) {
|
|||||||
}
|
}
|
||||||
$scope.connections = data;
|
$scope.connections = data;
|
||||||
});
|
});
|
||||||
$http.get('/rest/need').success(function (data) {
|
|
||||||
var i, name;
|
|
||||||
for (i = 0; i < data.length; i++) {
|
|
||||||
name = data[i].Name.split('/');
|
|
||||||
data[i].ShortName = name[name.length - 1];
|
|
||||||
}
|
|
||||||
data.sort(function (a, b) {
|
|
||||||
if (a.ShortName < b.ShortName) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (a.ShortName > b.ShortName) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
$scope.need = data;
|
|
||||||
});
|
|
||||||
$http.get('/rest/errors').success(function (data) {
|
$http.get('/rest/errors').success(function (data) {
|
||||||
$scope.errors = data;
|
$scope.errors = data;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.syncPercentage = function (repo) {
|
||||||
|
if (typeof $scope.model[repo] === 'undefined') {
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
if ($scope.model[repo].globalBytes === 0) {
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
var pct = 100 * $scope.model[repo].inSyncBytes / $scope.model[repo].globalBytes;
|
||||||
|
return Math.ceil(pct);
|
||||||
|
};
|
||||||
|
|
||||||
$scope.nodeStatus = function (nodeCfg) {
|
$scope.nodeStatus = function (nodeCfg) {
|
||||||
var conn = $scope.connections[nodeCfg.NodeID];
|
var conn = $scope.connections[nodeCfg.NodeID];
|
||||||
if (conn) {
|
if (conn) {
|
||||||
@ -182,7 +164,7 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http) {
|
|||||||
if (conn) {
|
if (conn) {
|
||||||
return conn.Address;
|
return conn.Address;
|
||||||
}
|
}
|
||||||
return '(unknown address)';
|
return '?';
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.nodeCompletion = function (nodeCfg) {
|
$scope.nodeCompletion = function (nodeCfg) {
|
||||||
@ -201,7 +183,7 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http) {
|
|||||||
if (conn) {
|
if (conn) {
|
||||||
return conn.ClientVersion;
|
return conn.ClientVersion;
|
||||||
}
|
}
|
||||||
return '(unknown version)';
|
return '?';
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.nodeName = function (nodeCfg) {
|
$scope.nodeName = function (nodeCfg) {
|
||||||
@ -211,11 +193,15 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http) {
|
|||||||
return nodeCfg.NodeID.substr(0, 6);
|
return nodeCfg.NodeID.substr(0, 6);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.editSettings = function () {
|
||||||
|
$('#settings').modal({backdrop: 'static', keyboard: true});
|
||||||
|
}
|
||||||
|
|
||||||
$scope.saveSettings = function () {
|
$scope.saveSettings = function () {
|
||||||
$scope.configInSync = false;
|
$scope.configInSync = false;
|
||||||
$scope.config.Options.ListenAddress = $scope.config.Options.ListenStr.split(',').map(function (x) { return x.trim(); });
|
$scope.config.Options.ListenAddress = $scope.config.Options.ListenStr.split(',').map(function (x) { return x.trim(); });
|
||||||
$http.post('/rest/config', JSON.stringify($scope.config), {headers: {'Content-Type': 'application/json'}});
|
$http.post('/rest/config', JSON.stringify($scope.config), {headers: {'Content-Type': 'application/json'}});
|
||||||
$('#settingsTable').collapse('hide');
|
$('#settings').modal("hide");
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.restart = function () {
|
$scope.restart = function () {
|
||||||
@ -224,34 +210,34 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
$scope.editNode = function (nodeCfg) {
|
$scope.editNode = function (nodeCfg) {
|
||||||
$scope.currentNode = nodeCfg;
|
$scope.currentNode = $.extend({}, nodeCfg);
|
||||||
$scope.editingExisting = true;
|
$scope.editingExisting = true;
|
||||||
$scope.currentNode.AddressesStr = nodeCfg.Addresses.join(', ');
|
$scope.currentNode.AddressesStr = nodeCfg.Addresses.join(', ');
|
||||||
$('#editNode').modal({backdrop: 'static', keyboard: false});
|
$('#editNode').modal({backdrop: 'static', keyboard: true});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.addNode = function () {
|
$scope.addNode = function () {
|
||||||
$scope.currentNode = {NodeID: '', AddressesStr: 'dynamic'};
|
$scope.currentNode = {AddressesStr: 'dynamic'};
|
||||||
$scope.editingExisting = false;
|
$scope.editingExisting = false;
|
||||||
$('#editNode').modal({backdrop: 'static', keyboard: false});
|
$('#editNode').modal({backdrop: 'static', keyboard: true});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.deleteNode = function () {
|
$scope.deleteNode = function () {
|
||||||
var newNodes = [], i;
|
|
||||||
|
|
||||||
$('#editNode').modal('hide');
|
$('#editNode').modal('hide');
|
||||||
if (!$scope.editingExisting) {
|
if (!$scope.editingExisting) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < $scope.nodes.length; i++) {
|
$scope.nodes = $scope.nodes.filter(function (n) {
|
||||||
if ($scope.nodes[i].NodeID !== $scope.currentNode.NodeID) {
|
return n.NodeID !== $scope.currentNode.NodeID;
|
||||||
newNodes.push($scope.nodes[i]);
|
});
|
||||||
}
|
$scope.config.Nodes = $scope.nodes;
|
||||||
}
|
|
||||||
|
|
||||||
$scope.nodes = newNodes;
|
for (var i = 0; i < $scope.repos.length; i++) {
|
||||||
$scope.config.Repositories[0].Nodes = newNodes;
|
$scope.repos[i].Nodes = $scope.repos[i].Nodes.filter(function (n) {
|
||||||
|
return n.NodeID !== $scope.currentNode.NodeID;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
$scope.configInSync = false;
|
$scope.configInSync = false;
|
||||||
$http.post('/rest/config', JSON.stringify($scope.config), {headers: {'Content-Type': 'application/json'}});
|
$http.post('/rest/config', JSON.stringify($scope.config), {headers: {'Content-Type': 'application/json'}});
|
||||||
@ -279,21 +265,15 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$scope.nodes.sort(nodeCompare);
|
$scope.nodes.sort(nodeCompare);
|
||||||
$scope.config.Repositories[0].Nodes = $scope.nodes;
|
$scope.config.Nodes = $scope.nodes;
|
||||||
|
|
||||||
$http.post('/rest/config', JSON.stringify($scope.config), {headers: {'Content-Type': 'application/json'}});
|
$http.post('/rest/config', JSON.stringify($scope.config), {headers: {'Content-Type': 'application/json'}});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.otherNodes = function () {
|
$scope.otherNodes = function () {
|
||||||
var nodes = [], i, n;
|
return $scope.nodes.filter(function (n){
|
||||||
|
return n.NodeID !== $scope.myID;
|
||||||
for (i = 0; i < $scope.nodes.length; i++) {
|
});
|
||||||
n = $scope.nodes[i];
|
|
||||||
if (n.NodeID !== $scope.myID) {
|
|
||||||
nodes.push(n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nodes;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.thisNode = function () {
|
$scope.thisNode = function () {
|
||||||
@ -308,14 +288,9 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
$scope.errorList = function () {
|
$scope.errorList = function () {
|
||||||
var errors = [];
|
return $scope.errors.filter(function (e) {
|
||||||
for (var i = 0; i < $scope.errors.length; i++) {
|
return e.Time > $scope.seenError;
|
||||||
var e = $scope.errors[i];
|
});
|
||||||
if (e.Time > $scope.seenError) {
|
|
||||||
errors.push(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return errors;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.clearErrors = function () {
|
$scope.clearErrors = function () {
|
||||||
@ -330,7 +305,96 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http) {
|
|||||||
return str;
|
return str;
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.refresh();
|
$scope.editRepo = function (nodeCfg) {
|
||||||
|
$scope.currentRepo = $.extend({selectedNodes: {}}, nodeCfg);
|
||||||
|
$scope.currentRepo.Nodes.forEach(function (n) {
|
||||||
|
$scope.currentRepo.selectedNodes[n.NodeID] = true;
|
||||||
|
});
|
||||||
|
$scope.editingExisting = true;
|
||||||
|
$('#editRepo').modal({backdrop: 'static', keyboard: true});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.addRepo = function () {
|
||||||
|
$scope.currentRepo = {selectedNodes: {}};
|
||||||
|
$scope.editingExisting = false;
|
||||||
|
$('#editRepo').modal({backdrop: 'static', keyboard: true});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.saveRepo = function () {
|
||||||
|
var repoCfg, done, i;
|
||||||
|
|
||||||
|
$scope.configInSync = false;
|
||||||
|
$('#editRepo').modal('hide');
|
||||||
|
repoCfg = $scope.currentRepo;
|
||||||
|
repoCfg.Nodes = [];
|
||||||
|
repoCfg.selectedNodes[$scope.myID] = true;
|
||||||
|
for (var nodeID in repoCfg.selectedNodes) {
|
||||||
|
if (repoCfg.selectedNodes[nodeID] === true) {
|
||||||
|
repoCfg.Nodes.push({NodeID: nodeID});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete repoCfg.selectedNodes;
|
||||||
|
|
||||||
|
done = false;
|
||||||
|
for (i = 0; i < $scope.repos.length; i++) {
|
||||||
|
if ($scope.repos[i].ID === repoCfg.ID) {
|
||||||
|
$scope.repos[i] = repoCfg;
|
||||||
|
done = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!done) {
|
||||||
|
$scope.repos.push(repoCfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.config.Repositories = $scope.repos;
|
||||||
|
|
||||||
|
$http.post('/rest/config', JSON.stringify($scope.config), {headers: {'Content-Type': 'application/json'}});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.deleteRepo = function () {
|
||||||
|
$('#editRepo').modal('hide');
|
||||||
|
if (!$scope.editingExisting) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.repos = $scope.repos.filter(function (r) {
|
||||||
|
return r.ID !== $scope.currentRepo.ID;
|
||||||
|
});
|
||||||
|
|
||||||
|
$scope.config.Repositories = $scope.repos;
|
||||||
|
|
||||||
|
$scope.configInSync = false;
|
||||||
|
$http.post('/rest/config', JSON.stringify($scope.config), {headers: {'Content-Type': 'application/json'}});
|
||||||
|
};
|
||||||
|
|
||||||
|
$http.get('/rest/version').success(function (data) {
|
||||||
|
$scope.version = data;
|
||||||
|
});
|
||||||
|
|
||||||
|
$http.get('/rest/system').success(function (data) {
|
||||||
|
$scope.system = data;
|
||||||
|
$scope.myID = data.myID;
|
||||||
|
});
|
||||||
|
|
||||||
|
$http.get('/rest/config').success(function (data) {
|
||||||
|
$scope.config = data;
|
||||||
|
$scope.config.Options.ListenStr = $scope.config.Options.ListenAddress.join(', ');
|
||||||
|
|
||||||
|
var nodes = $scope.config.Nodes;
|
||||||
|
nodes.sort(nodeCompare);
|
||||||
|
$scope.nodes = nodes;
|
||||||
|
|
||||||
|
$scope.repos = $scope.config.Repositories;
|
||||||
|
|
||||||
|
$scope.refresh();
|
||||||
|
});
|
||||||
|
|
||||||
|
$http.get('/rest/config/sync').success(function (data) {
|
||||||
|
$scope.configInSync = data.configInSync;
|
||||||
|
});
|
||||||
|
|
||||||
setInterval($scope.refresh, 10000);
|
setInterval($scope.refresh, 10000);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
14
gui/bootstrap/css/bootstrap.min.css
vendored
Executable file → Normal file
14
gui/bootstrap/css/bootstrap.min.css
vendored
Executable file → Normal file
File diff suppressed because one or more lines are too long
675
gui/index.html
675
gui/index.html
@ -1,281 +1,470 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en" ng-app="syncthing">
|
<html lang="en" ng-app="syncthing">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<meta name="description" content="">
|
<meta name="description" content="">
|
||||||
<meta name="author" content="">
|
<meta name="author" content="">
|
||||||
<link rel="shortcut icon" href="favicon.png">
|
<link rel="shortcut icon" href="favicon.png">
|
||||||
|
|
||||||
<title>syncthing</title>
|
<title>syncthing</title>
|
||||||
<link href="bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
<link href="bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
|
body {
|
||||||
|
padding-bottom: 70px;
|
||||||
|
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
h1, h2, h3, h4, h5, h6 {
|
||||||
padding-top: 70px;
|
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||||
padding-bottom: 70px;
|
font-weight: 300;
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-monospace {
|
a.btn {
|
||||||
font-family: monospace;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-condensed>thead>tr>th, .table-condensed>tbody>tr>th, .table-condensed>tfoot>tr>th, .table-condensed>thead>tr>td, .table-condensed>tbody>tr>td, .table-condensed>tfoot>tr>td {
|
ul+h5 {
|
||||||
border-top: none;
|
margin-top: 1.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
thead tr th {
|
.text-monospace {
|
||||||
text-align: center;
|
font-family: monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo {
|
.table-condensed>thead>tr>th, .table-condensed>tbody>tr>th, .table-condensed>tfoot>tr>th, .table-condensed>thead>tr>td, .table-condensed>tbody>tr>td, .table-condensed>tfoot>tr>td {
|
||||||
margin: 0;
|
border-top: none;
|
||||||
padding: 0;
|
}
|
||||||
top: -5px;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
thead tr th {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
top: -5px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress {
|
||||||
|
height: 21px;
|
||||||
|
margin-bottom: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress .progress-bar {
|
||||||
|
line-height: 21px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapsed-visible {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.collapsed .collapsed-visible {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-no-bullet {
|
||||||
|
list-style-type: none
|
||||||
|
}
|
||||||
|
|
||||||
|
.li-column {
|
||||||
|
display: inline-block;
|
||||||
|
min-width: 7em;
|
||||||
|
margin-right: 1em;
|
||||||
|
background-color: rgb(236, 240, 241);
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 1px 4px;
|
||||||
|
margin: 2px 2px;
|
||||||
|
}
|
||||||
|
.li-column span.data {
|
||||||
|
margin-left: 0.5em;
|
||||||
|
min-width: 10em;
|
||||||
|
text-align: right;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body ng-controller="SyncthingCtrl">
|
<body ng-controller="SyncthingCtrl">
|
||||||
<div class="navbar navbar-fixed-top navbar-default">
|
|
||||||
<div class="container">
|
|
||||||
<a class="navbar-brand"><img class="logo" src="st-logo-128.png" width="32" height="32"> Syncthing</a>
|
|
||||||
<div ng-if="!configInSync">
|
|
||||||
<form class="navbar-form navbar-right">
|
|
||||||
<button type="button" class="btn btn-primary" ng-click="restart()">Restart Now</button>
|
|
||||||
</form>
|
|
||||||
<p class="navbar-text navbar-right">The configuration has been changed but not activated. Syncthing must restart to activate the new configuration.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="container">
|
<!-- Top bar -->
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-12">
|
<nav class="navbar navbar-top navbar-default" role="navigation">
|
||||||
<div ng-if="errorList().length > 0" class="alert alert-warning">
|
<div class="container">
|
||||||
<p ng-repeat="err in errorList()"><small>{{err.Time | date:"hh:mm:ss.sss"}}:</small> {{friendlyNodes(err.Error)}}</p>
|
<span class="navbar-brand"><img class="logo" src="st-logo-128.png" width="32" height="32"> Syncthing</span>
|
||||||
<button type="button" class="pull-right btn btn-warning" ng-click="clearErrors()">OK</button>
|
<button type="button" class="btn btn-default btn-sm pull-right navbar-btn" ng-click="editSettings()"><span class="glyphicon glyphicon-cog"></span> Settings</button>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
|
||||||
|
<!-- First row, only shown if necessary; Restart warning -->
|
||||||
|
|
||||||
|
<div ng-if="!configInSync" class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="panel panel-warning">
|
||||||
|
<div class="panel-heading"><h3 class="panel-title">Restart Needed</h3></div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<p>The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.</p>
|
||||||
|
</div>
|
||||||
|
<div class="panel-footer">
|
||||||
|
<button type="button" class="btn btn-sm btn-default pull-right" ng-click="restart()"><span class="glyphicon glyphicon-off"></span> Restart Now</button>
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="panel panel-info">
|
|
||||||
<div class="panel-heading"><h3 class="panel-title">Cluster</h3></div>
|
|
||||||
<table class="table table-condensed">
|
|
||||||
<tbody>
|
|
||||||
<!-- myself -->
|
|
||||||
<tr class="text-muted" ng-repeat="nodeCfg in thisNode()">
|
|
||||||
<td style="width:12%">
|
|
||||||
<span class="label label-default">
|
|
||||||
<span class="glyphicon glyphicon-ok"></span> This node
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td style="width:10%">
|
|
||||||
<span class="text-monospace">{{nodeName(nodeCfg)}}</span>
|
|
||||||
</td>
|
|
||||||
<td style="width:20%">{{version}}</td>
|
|
||||||
<td style="width:25%">(this node)</td>
|
|
||||||
<td style="width:9%" class="text-right">
|
|
||||||
{{inbps | metric}}bps
|
|
||||||
<span class="text-muted glyphicon glyphicon-chevron-down"></span>
|
|
||||||
</td>
|
|
||||||
<td style="width:9%" class="text-right">
|
|
||||||
{{outbps | metric}}bps
|
|
||||||
<span class="text-muted glyphicon glyphicon-chevron-up"></span>
|
|
||||||
</td>
|
|
||||||
<td style="width:7%" class="text-right">
|
|
||||||
<button type="button" ng-click="editNode(nodeCfg)" class="btn btn-default btn-xs"><span class="glyphicon glyphicon-pencil"></span> Edit</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<!-- all other nodes -->
|
|
||||||
<tr ng-repeat="nodeCfg in otherNodes()">
|
|
||||||
<td>
|
|
||||||
<span class="label label-{{nodeClass(nodeCfg)}}">
|
|
||||||
<span class="glyphicon glyphicon-{{nodeIcon(nodeCfg)}}"></span> {{nodeStatus(nodeCfg)}}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<span class="text-monospace">{{nodeName(nodeCfg)}}</span>
|
|
||||||
</td>
|
|
||||||
<td>{{nodeVer(nodeCfg)}}</td>
|
|
||||||
<td>{{nodeAddr(nodeCfg)}}</td>
|
|
||||||
<td class="text-right">
|
|
||||||
<abbr title="{{connections[nodeCfg.NodeID].InBytesTotal | binary}}B">{{connections[nodeCfg.NodeID].inbps | metric}}bps</abbr>
|
|
||||||
<span class="text-muted glyphicon glyphicon-chevron-down"></span>
|
|
||||||
</td>
|
|
||||||
<td class="text-right">
|
|
||||||
<abbr title="{{connections[nodeCfg.NodeID].OutBytesTotal | binary}}B">{{connections[nodeCfg.NodeID].outbps | metric}}bps</abbr>
|
|
||||||
<span class="text-muted glyphicon glyphicon-chevron-up"></span>
|
|
||||||
</td>
|
|
||||||
<td class="text-right">
|
|
||||||
<button type="button" ng-click="editNode(nodeCfg)" class="btn btn-default btn-xs"><span class="glyphicon glyphicon-pencil"></span> Edit</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
<td class="text-right">
|
|
||||||
<button type="button" class="btn btn-default btn-xs" ng-click="addNode()"><span class="glyphicon glyphicon-plus"></span> Add</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- First regular row -->
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="panel panel-info">
|
|
||||||
<div class="panel-heading"><h3 class="panel-title">Repository</h3></div>
|
|
||||||
<div class="panel-body">
|
|
||||||
<p>Cluster contains {{model.globalFiles | alwaysNumber}} files, {{model.globalBytes | binary}}B
|
|
||||||
<span class="text-muted">(+{{model.globalDeleted | alwaysNumber}} delete records)</span></p>
|
|
||||||
|
|
||||||
<p>Local repository has {{model.localFiles | alwaysNumber}} files, {{model.localBytes | binary}}B
|
<!-- Repository list (top left) -->
|
||||||
<span class="text-muted">(+{{model.localDeleted | alwaysNumber}} delete records)</span></p>
|
|
||||||
</div>
|
<div class="col-md-6">
|
||||||
</div>
|
<div class="panel panel-default">
|
||||||
</div>
|
<div class="panel-heading"><h3 class="panel-title">Repositories</h3></div>
|
||||||
<div class="col-md-6">
|
<div class="panel-body">
|
||||||
<div class="panel" ng-class="{'panel-success': model.needBytes === 0, 'panel-primary': model.needBytes !== 0}">
|
<ul class="list-unstyled" ng-repeat="repo in repos">
|
||||||
<div class="panel-heading"><h3 class="panel-title">Synchronization</h3></div>
|
<li>
|
||||||
<div class="panel-body">
|
<span class="text-monospace">{{repo.Directory}}</span>
|
||||||
<div class="progress">
|
<ul class="list-no-bullet">
|
||||||
<div class="progress-bar" role="progressbar" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100"
|
<li>
|
||||||
ng-class="{'progress-bar-success': model.needBytes === 0, 'progress-bar-info': model.needBytes !== 0}"
|
<div class="li-column">
|
||||||
ng-style="{width: (100 * model.inSyncBytes / model.globalBytes) + '%'}">
|
<span class="text-muted glyphicon glyphicon-tag"></span>
|
||||||
{{100 * model.inSyncBytes / model.globalBytes | alwaysNumber | number:0}}%
|
<span class="data">{{repo.ID}}</span>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<p ng-show="model.needBytes > 0">Need {{model.needFiles | alwaysNumber}} files, {{model.needBytes | binary}}B</p>
|
<div class="li-column">
|
||||||
</div>
|
<span class="text-muted glyphicon glyphicon-home"></span>
|
||||||
</div>
|
<span class="data text-success" ng-show="syncPercentage(repo.ID) == 100">In Sync</span>
|
||||||
|
<span class="data text-primary" ng-hide="syncPercentage(repo.ID) == 100">Syncing ({{syncPercentage(repo.ID)}}%)</span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<div class="li-column">
|
||||||
|
<span class="text-muted glyphicon glyphicon-globe"></span>
|
||||||
|
<span class="data">{{model[repo.ID].globalFiles | alwaysNumber}} files, {{model[repo.ID].globalBytes | binary}}B</span>
|
||||||
|
</div>
|
||||||
|
<div class="li-column">
|
||||||
|
<span class="text-muted glyphicon glyphicon-home"></span>
|
||||||
|
<span class="data">{{model[repo.ID].localFiles | alwaysNumber}} files, {{model[repo.ID].localBytes | binary}}B</span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<div class="li-column">
|
||||||
|
<span class="text-muted glyphicon glyphicon-cloud-download"></span>
|
||||||
|
<span class="data">{{model[repo.ID].needFiles | alwaysNumber}} files, {{model[repo.ID].needBytes | binary}}B</span>
|
||||||
|
</div>
|
||||||
|
<div class="li-column">
|
||||||
|
<span class="text-muted glyphicon glyphicon-cog"></span>
|
||||||
|
<span class="data"><a href="" ng-click="editRepo(repo)"><span class="glyphicon glyphicon-pencil"></span> Edit</a></span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="panel-footer">
|
||||||
|
<button type="button" class="pull-right btn btn-sm btn-default" ng-click="addRepo()"><span class="glyphicon glyphicon-plus"></span> Add Repository</button>
|
||||||
|
<div class="clearfix"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Node list (top right) -->
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading"><h3 class="panel-title">Nodes</h3></div>
|
||||||
|
<div class="panel-body">
|
||||||
|
|
||||||
|
<h5>Peer Nodes</h5>
|
||||||
|
<ul class="list-unstyled" ng-repeat="nodeCfg in otherNodes()">
|
||||||
|
<li>
|
||||||
|
<span class="text-monospace">{{nodeName(nodeCfg)}}</span>
|
||||||
|
<ul class="list-no-bullet">
|
||||||
|
<li>
|
||||||
|
<div class="li-column">
|
||||||
|
<span class="text-muted glyphicon glyphicon-link"></span>
|
||||||
|
<span class="data">{{nodeAddr(nodeCfg)}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="li-column">
|
||||||
|
<span class="text-muted glyphicon glyphicon-dashboard"></span>
|
||||||
|
<span class="data text-{{nodeClass(nodeCfg)}}">{{nodeStatus(nodeCfg)}}</span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<div class="li-column">
|
||||||
|
<span class="text-muted glyphicon glyphicon-cloud-download"></span>
|
||||||
|
<span class="data">{{connections[nodeCfg.NodeID].inbps | metric}}bps</span>
|
||||||
|
</div>
|
||||||
|
<div class="li-column">
|
||||||
|
<span class="text-muted glyphicon glyphicon-cloud-upload"></span>
|
||||||
|
<span class="data">{{connections[nodeCfg.NodeID].outbps | metric}}bps</span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<div class="li-column">
|
||||||
|
<span class="text-muted glyphicon glyphicon-tag"></span>
|
||||||
|
<span class="data">{{nodeVer(nodeCfg)}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="li-column">
|
||||||
|
<span class="text-muted glyphicon glyphicon-cog"></span>
|
||||||
|
<span class="data"><a href="" ng-click="editNode(nodeCfg)"><span class="glyphicon glyphicon-pencil"></span> Edit</a></span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h5>This Node</h5>
|
||||||
|
<ul class="list-unstyled" ng-repeat="nodeCfg in thisNode()">
|
||||||
|
<li>
|
||||||
|
<span class="text-monospace">{{nodeName(nodeCfg)}}</span> 
|
||||||
|
<ul class="list-no-bullet">
|
||||||
|
<li>
|
||||||
|
<div class="li-column">
|
||||||
|
<span class="text-muted glyphicon glyphicon-th"></span>
|
||||||
|
<span class="data">{{system.sys | binary}}B RAM</span>
|
||||||
|
</div>
|
||||||
|
<div class="li-column">
|
||||||
|
<span class="text-muted glyphicon glyphicon-tasks"></span>
|
||||||
|
<span class="data">{{system.cpuPercent | alwaysNumber | natural:1}}% CPU</span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<div class="li-column">
|
||||||
|
<span class="text-muted glyphicon glyphicon-cloud-download"></span>
|
||||||
|
<span class="data">{{inbps | metric}}bps</span>
|
||||||
|
</div>
|
||||||
|
<div class="li-column">
|
||||||
|
<span class="text-muted glyphicon glyphicon-cloud-upload"></span>
|
||||||
|
<span class="data">{{outbps | metric}}bps</span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<div class="li-column">
|
||||||
|
<span class="text-muted glyphicon glyphicon-cog"></span>
|
||||||
|
<span class="data"><a href="" ng-click="editNode(nodeCfg)"><span class="glyphicon glyphicon-pencil"></span> Edit</a></span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="panel-footer">
|
||||||
|
<button type="button" class="pull-right btn btn-sm btn-default" ng-click="addNode()"><span class="glyphicon glyphicon-plus"></span> Add Node</button>
|
||||||
|
<div class="clearfix"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div> <!-- /row -->
|
||||||
|
|
||||||
|
<!-- Errors -->
|
||||||
|
|
||||||
|
<div ng-if="errorList().length > 0" class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="panel panel-warning">
|
||||||
|
<div class="panel-heading"><h3 class="panel-title">Notice</h3></div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<p ng-repeat="err in errorList()"><small>{{err.Time | date:"hh:mm:ss.sss"}}:</small> {{friendlyNodes(err.Error)}}</p>
|
||||||
|
</div>
|
||||||
|
<div class="panel-footer">
|
||||||
|
<button type="button" class="pull-right btn btn-sm btn-default" ng-click="clearErrors()">OK</button>
|
||||||
|
<div class="clearfix"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
</div> <!-- /container -->
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="panel panel-info">
|
|
||||||
<div class="panel-heading"><h3 class="panel-title"><a href="" data-toggle="collapse" data-target="#system">System</a></h3></div>
|
|
||||||
<div id="system" class="panel-collapse collapse">
|
|
||||||
<div class="panel-body">
|
|
||||||
<p>{{system.sys | binary}}B RAM allocated, {{system.alloc | binary}}B in use</p>
|
|
||||||
<p>{{system.cpuPercent | alwaysNumber | natural:1}}% CPU, {{system.goroutines | alwaysNumber}} goroutines</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="panel panel-info">
|
|
||||||
<div class="panel-heading"><h3 class="panel-title"><a href="" data-toggle="collapse" data-target="#settingsTable">Settings</a></h3></div>
|
|
||||||
<div id="settingsTable" class="panel-collapse collapse">
|
|
||||||
<div class="panel-body">
|
|
||||||
<form role="form">
|
|
||||||
<div class="form-group" ng-repeat="setting in settings">
|
|
||||||
<div ng-if="setting.type == 'text' || setting.type == 'number'">
|
|
||||||
<label for="{{setting.id}}">{{setting.descr}}</label>
|
|
||||||
<input id="{{setting.id}}" class="form-control" type="{{setting.type}}" ng-model="config.Options[setting.id]"></input>
|
|
||||||
</div>
|
|
||||||
<div class="checkbox" ng-if="setting.type == 'bool'">
|
|
||||||
<label>
|
|
||||||
{{setting.descr}} <input id="{{setting.id}}" type="checkbox" ng-model="config.Options[setting.id]"></input>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div class="panel-footer">
|
|
||||||
<button type="button" class="btn btn-sm btn-default" ng-click="saveSettings()">Save</button>
|
|
||||||
<small><span class="text-muted">Changes take effect when restarting syncthing.</span></small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="navbar navbar-default navbar-fixed-bottom">
|
<!-- Bottom bar -->
|
||||||
|
|
||||||
|
<nav class="navbar navbar-default navbar-fixed-bottom hidden-xs">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<p class="navbar-text">{{version}}</p>
|
<p class="navbar-text">{{version}}</p>
|
||||||
<ul class="nav navbar-nav navbar-right">
|
<ul class="nav navbar-nav navbar-right">
|
||||||
<li><a class="navbar-link" href="http://discourse.syncthing.net/">Support / Forum</a></li>
|
<li><a class="navbar-link" href="http://discourse.syncthing.net/">Support / Forum</a></li>
|
||||||
<li><a class="navbar-link" href="https://github.com/calmh/syncthing/releases">Latest Release</a></li>
|
<li><a class="navbar-link hidden-sm" href="https://github.com/calmh/syncthing/releases">Latest Release</a></li>
|
||||||
<li><a class="navbar-link" href="https://github.com/calmh/syncthing/wiki">Documentation</a></li>
|
<li><a class="navbar-link" href="https://github.com/calmh/syncthing/wiki">Documentation</a></li>
|
||||||
<li><a class="navbar-link" href="https://github.com/calmh/syncthing/issues">Bugs</a></li>
|
<li><a class="navbar-link hidden-sm" href="https://github.com/calmh/syncthing/issues">Bugs</a></li>
|
||||||
<li><a class="navbar-link" href="https://github.com/calmh/syncthing">Source Code</a></li>
|
<li><a class="navbar-link hidden-sm" href="https://github.com/calmh/syncthing">Source Code</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</nav>
|
||||||
|
|
||||||
<div id="networkError" class="modal fade">
|
<!-- Network error modal -->
|
||||||
|
|
||||||
|
<div id="networkError" class="modal fade">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header alert alert-danger">
|
<div class="modal-header alert alert-danger">
|
||||||
<h4 class="modal-title">
|
<h4 class="modal-title">
|
||||||
<span class="glyphicon glyphicon-exclamation-sign"></span>
|
<span class="glyphicon glyphicon-exclamation-sign"></span>
|
||||||
Connection Error
|
Connection Error
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<p>
|
|
||||||
Syncthing seems to be down, or there is a problem with your Internet connection.
|
|
||||||
Retrying…
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p>
|
||||||
|
Syncthing seems to be down, or there is a problem with your Internet connection.
|
||||||
|
Retrying…
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="editNode" class="modal fade">
|
<!-- Node editor modal -->
|
||||||
|
|
||||||
|
<div id="editNode" class="modal fade">
|
||||||
<div class="modal-dialog modal-lg">
|
<div class="modal-dialog modal-lg">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||||
<h4 class="modal-title">Edit Node</h4>
|
<h4 ng-show="!editingExisting" class="modal-title">Add Node</h4>
|
||||||
</div>
|
<h4 ng-show="editingExisting" class="modal-title">Edit Node</h4>
|
||||||
<div class="modal-body">
|
|
||||||
<form role="form">
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="nodeID">Node ID</label>
|
|
||||||
<input placeholder="YUFJOUDPORCMA..." ng-disabled="editingExisting" id="nodeID" class="form-control" type="text" ng-model="currentNode.NodeID"></input>
|
|
||||||
<p class="help-block">The node ID can be found in the logs or in the "Add Node" dialog on the other node.</p>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="name">Name</label>
|
|
||||||
<input placeholder="Home Server" id="name" class="form-control" type="text" ng-model="currentNode.Name"></input>
|
|
||||||
<p class="help-block">Shown instead of Node ID in the cluster status.</p>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="addresses">Addresses</label>
|
|
||||||
<input placeholder="dynamic" ng-disabled="currentNode.NodeID == myID" id="addresses" class="form-control" type="text" ng-model="currentNode.AddressesStr"></input>
|
|
||||||
<p class="help-block">Enter comma separated <span class="text-monospace">ip:port</span> addresses or <span class="text-monospace">dynamic</span> to perform automatic discovery of the address.</p>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<div ng-show="!editingExisting">
|
|
||||||
When adding a new node, keep in mind that <em>this node</em> must be added on the other side too. The Node ID of this node is:
|
|
||||||
<pre>{{myID}}</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-primary" ng-click="saveNode()">Save</button>
|
|
||||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
|
||||||
<button ng-if="editingExisting" type="button" class="btn btn-danger pull-left" ng-click="deleteNode()">Delete</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form role="form">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="nodeID">Node ID</label>
|
||||||
|
<input ng-disabled="editingExisting" id="nodeID" class="form-control" type="text" ng-model="currentNode.NodeID"></input>
|
||||||
|
<p class="help-block">The node ID can be found in the logs or in the "Add Node" dialog on the other node.</p>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="name">Name</label>
|
||||||
|
<input placeholder="Home Server" id="name" class="form-control" type="text" ng-model="currentNode.Name"></input>
|
||||||
|
<p class="help-block">Shown instead of Node ID in the cluster status.</p>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="addresses">Addresses</label>
|
||||||
|
<input placeholder="dynamic" ng-disabled="currentNode.NodeID == myID" id="addresses" class="form-control" type="text" ng-model="currentNode.AddressesStr"></input>
|
||||||
|
<p class="help-block">Enter comma separated <span class="text-monospace">ip:port</span> addresses or <span class="text-monospace">dynamic</span> to perform automatic discovery of the address.</p>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div ng-show="!editingExisting">
|
||||||
|
When adding a new node, keep in mind that <em>this node</em> must be added on the other side too. The Node ID of this node is:
|
||||||
|
<pre>{{myID}}</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-primary" ng-click="saveNode()">Save</button>
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||||
|
<button ng-if="editingExisting" type="button" class="btn btn-danger pull-left" ng-click="deleteNode()">Delete</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="angular.min.js"></script>
|
<!-- Repo editor modal -->
|
||||||
<script src="jquery-2.0.3.min.js"></script>
|
|
||||||
<script src="bootstrap/js/bootstrap.min.js"></script>
|
<div id="editRepo" class="modal fade">
|
||||||
<script src="app.js"></script>
|
<div class="modal-dialog modal-lg">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||||
|
<h4 ng-show="!editingExisting" class="modal-title">Add Repository</h4>
|
||||||
|
<h4 ng-show="editingExisting" class="modal-title">Edit Repository</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form role="form">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="repoID">Repository ID</label>
|
||||||
|
<input placeholder="documents" ng-disabled="editingExisting" id="repoID" class="form-control" type="text" ng-model="currentRepo.ID"></input>
|
||||||
|
<p class="help-block">Short, unique identifier for the repository. Must be the same on all cluster nodes.</p>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="repoPath">Repository Path</label>
|
||||||
|
<input placeholder="~/Documents" id="repoPath" class="form-control" type="text" ng-model="currentRepo.Directory"></input>
|
||||||
|
<p class="help-block">Path to the repository on the local computer. Will be created if it does not exist.</p>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="checkbox">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" ng-model="currentRepo.ReadOnly"> Read Only
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="nodes">Nodes</label>
|
||||||
|
<div class="checkbox" ng-repeat="node in otherNodes()">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" ng-model="currentRepo.selectedNodes[node.NodeID]"> {{nodeName(node)}}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<p class="help-block">Select the nodes to share this repository with.</p>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div ng-show="!editingExisting">
|
||||||
|
When adding a new repository, keep in mind that the Repository ID is used to tie repositories together between nodes. They are case sensitive and must match exactly between all nodes.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-primary" ng-click="saveRepo()">Save</button>
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||||
|
<button ng-if="editingExisting" type="button" class="btn btn-danger pull-left" ng-click="deleteRepo()">Delete</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Settings modal -->
|
||||||
|
|
||||||
|
<div id="settings" class="modal fade">
|
||||||
|
<div class="modal-dialog modal-lg">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||||
|
<h4 class="modal-title"> Settings</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form role="form">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group" ng-repeat="setting in settings">
|
||||||
|
<div ng-if="setting.type == 'text' || setting.type == 'number'">
|
||||||
|
<label for="{{setting.id}}">{{setting.descr}}</label>
|
||||||
|
<input id="{{setting.id}}" class="form-control" type="{{setting.type}}" ng-model="config.Options[setting.id]"></input>
|
||||||
|
</div>
|
||||||
|
<div class="checkbox" ng-if="setting.type == 'bool'">
|
||||||
|
<label>
|
||||||
|
{{setting.descr}} <input id="{{setting.id}}" type="checkbox" ng-model="config.Options[setting.id]"></input>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group" ng-repeat="setting in guiSettings">
|
||||||
|
<div ng-if="setting.type == 'text' || setting.type == 'number' || setting.type == 'password'">
|
||||||
|
<label for="{{setting.id}}">{{setting.descr}}</label>
|
||||||
|
<input id="{{setting.id}}" class="form-control" type="{{setting.type}}" ng-model="config.GUI[setting.id]"></input>
|
||||||
|
</div>
|
||||||
|
<div class="checkbox" ng-if="setting.type == 'bool'">
|
||||||
|
<label>
|
||||||
|
{{setting.descr}} <input id="{{setting.id}}" type="checkbox" ng-model="config.GUI[setting.id]"></input>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-primary" ng-click="saveSettings()">Save</button>
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<script src="angular.min.js"></script>
|
||||||
|
<script src="jquery-2.0.3.min.js"></script>
|
||||||
|
<script src="bootstrap/js/bootstrap.min.js"></script>
|
||||||
|
<script src="app.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user