summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndré Colomb <src@andre.colomb.de>2021-06-07 09:08:44 +0200
committerGitHub <noreply@github.com>2021-06-07 09:08:44 +0200
commitea0a408849668e3ea1abd1f900ff4c2d1ba2b269 (patch)
tree95522ee7fc5c1bd27d7a0791b98dbb5290fae400
parent18592af993cbb40aad140b75483d8990193487e4 (diff)
gui: Modal dialog for listeners and discovery status (#7539)
-rw-r--r--gui/default/assets/lang/lang-en.json8
-rw-r--r--gui/default/index.html24
-rw-r--r--gui/default/syncthing/core/connectivityStatusModalView.html60
-rw-r--r--gui/default/syncthing/core/discoveryFailuresModalView.html21
-rwxr-xr-xgui/default/syncthing/core/syncthingController.js46
-rw-r--r--lib/api/api.go30
6 files changed, 139 insertions, 50 deletions
diff --git a/gui/default/assets/lang/lang-en.json b/gui/default/assets/lang/lang-en.json
index 8fbbf983f3..34c2e5c45a 100644
--- a/gui/default/assets/lang/lang-en.json
+++ b/gui/default/assets/lang/lang-en.json
@@ -286,6 +286,8 @@
"Sharing": "Sharing",
"Show ID": "Show ID",
"Show QR": "Show QR",
+ "Show detailed discovery status": "Show detailed discovery status",
+ "Show detailed listener status": "Show detailed listener status",
"Show diff with previous version": "Show diff with previous version",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.",
@@ -295,7 +297,9 @@
"Single level wildcard (matches within a directory only)": "Single level wildcard (matches within a directory only)",
"Size": "Size",
"Smallest First": "Smallest First",
+ "Some discovery methods could not be established for finding other devices or announcing this device:": "Some discovery methods could not be established for finding other devices or announcing this device:",
"Some items could not be restored:": "Some items could not be restored:",
+ "Some listening addresses could not be enabled to accept connections:": "Some listening addresses could not be enabled to accept connections:",
"Source Code": "Source Code",
"Stable releases and release candidates": "Stable releases and release candidates",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.",
@@ -312,6 +316,8 @@
"Syncthing has been shut down.": "Syncthing has been shut down.",
"Syncthing includes the following software or portions thereof:": "Syncthing includes the following software or portions thereof:",
"Syncthing is Free and Open Source Software licensed as MPL v2.0.": "Syncthing is Free and Open Source Software licensed as MPL v2.0.",
+ "Syncthing is listening on the following network addresses for connection attempts from other devices:": "Syncthing is listening on the following network addresses for connection attempts from other devices:",
+ "Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.": "Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.",
"Syncthing is restarting.": "Syncthing is restarting.",
"Syncthing is upgrading.": "Syncthing is upgrading.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.",
@@ -336,6 +342,7 @@
"The following intervals are used: for the first hour a version is kept every 30 seconds, for the first day a version is kept every hour, for the first 30 days a version is kept every day, until the maximum age a version is kept every week.": "The following intervals are used: for the first hour a version is kept every 30 seconds, for the first day a version is kept every hour, for the first 30 days a version is kept every day, until the maximum age a version is kept every week.",
"The following items could not be synchronized.": "The following items could not be synchronized.",
"The following items were changed locally.": "The following items were changed locally.",
+ "The following methods are used to discover other devices on the network and announce this device to be found by others:": "The following methods are used to discover other devices on the network and announce this device to be found by others:",
"The following unexpected items were found.": "The following unexpected items were found.",
"The interval must be a positive number of seconds.": "The interval must be a positive number of seconds.",
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.",
@@ -353,6 +360,7 @@
"They are retried automatically and will be synced when the error is resolved.": "They are retried automatically and will be synced when the error is resolved.",
"This Device": "This Device",
"This can easily give hackers access to read and change any files on your computer.": "This can easily give hackers access to read and change any files on your computer.",
+ "This device cannot automatically discover other devices or announce its own address to be found by others. Only devices with statically configured addresses can connect.": "This device cannot automatically discover other devices or announce its own address to be found by others. Only devices with statically configured addresses can connect.",
"This is a major version upgrade.": "This is a major version upgrade.",
"This setting controls the free space required on the home (i.e., index database) disk.": "This setting controls the free space required on the home (i.e., index database) disk.",
"Time": "Time",
diff --git a/gui/default/index.html b/gui/default/index.html
index 9758f3763a..2c42d33268 100644
--- a/gui/default/index.html
+++ b/gui/default/index.html
@@ -655,26 +655,20 @@
<tr>
<th><span class="fas fa-fw fa-sitemap"></span>&nbsp;<span translate>Listeners</span></th>
<td class="text-right">
- <span ng-if="listenersFailed.length == 0" class="data text-success">
- <span>{{listenersTotal}}/{{listenersTotal}}</span>
- </span>
- <span ng-if="listenersFailed.length != 0" class="data" ng-class="{'text-danger': listenersFailed.length == listenersTotal}">
- <span popover data-trigger="hover" data-placement="bottom" data-html="true" data-content="{{listenersFailed.join('<br>\n')}}">
- {{listenersTotal-listenersFailed.length}}/{{listenersTotal}}
- </span>
+ <span class="data" tooltip data-original-title="{{'Show detailed listener status' | translate}}.">
+ <a href="" style="color:inherit" ng-class="{'text-success': listenersFailed.length == 0, 'text-danger': listenersFailed.length == listenersTotal}" ng-click="showListenerStatus()">
+ {{listenersTotal-listenersFailed.length}}/{{listenersTotal}}
+ </a>
</span>
</td>
</tr>
<tr ng-if="system.discoveryEnabled">
<th><span class="fas fa-fw fa-map-signs"></span>&nbsp;<span translate>Discovery</span></th>
<td class="text-right">
- <span ng-if="discoveryFailed.length == 0" class="data text-success">
- <span>{{discoveryTotal}}/{{discoveryTotal}}</span>
- </span>
- <span ng-if="discoveryFailed.length != 0" class="data" ng-class="{'text-danger': discoveryFailed.length == discoveryTotal}">
- <span popover data-trigger="hover" data-placement="bottom" data-content="{{'Click to see discovery failures' | translate}}.">
- <a href="" style="color:inherit" ng-click="showDiscoveryFailures()">{{discoveryTotal-discoveryFailed.length}}/{{discoveryTotal}}</a>
- </span>
+ <span class="data" tooltip data-original-title="{{'Show detailed discovery status' | translate}}.">
+ <a href="" style="color:inherit" ng-class="{'text-success': discoveryFailed.length == 0, 'text-danger': discoveryFailed.length == discoveryTotal}" ng-click="showDiscoveryStatus()">
+ {{discoveryTotal-discoveryFailed.length}}/{{discoveryTotal}}
+ </a>
</span>
</td>
</tr>
@@ -921,7 +915,7 @@
<ng-include src="'syncthing/core/upgradeModalView.html'"></ng-include>
<ng-include src="'syncthing/core/majorUpgradeModalView.html'"></ng-include>
<ng-include src="'syncthing/core/aboutModalView.html'"></ng-include>
- <ng-include src="'syncthing/core/discoveryFailuresModalView.html'"></ng-include>
+ <ng-include src="'syncthing/core/connectivityStatusModalView.html'"></ng-include>
<ng-include src="'syncthing/folder/removeFolderDialogView.html'"></ng-include>
<ng-include src="'syncthing/folder/revertOverrideView.html'"></ng-include>
<ng-include src="'syncthing/device/removeDeviceDialogView.html'"></ng-include>
diff --git a/gui/default/syncthing/core/connectivityStatusModalView.html b/gui/default/syncthing/core/connectivityStatusModalView.html
new file mode 100644
index 0000000000..95c39c9d0e
--- /dev/null
+++ b/gui/default/syncthing/core/connectivityStatusModalView.html
@@ -0,0 +1,60 @@
+<modal id="connectivity-status" status="{{connectivityStatusParams.status}}" icon="fas fa-fw {{connectivityStatusParams.type == 'listeners' ? 'fa-sitemap' : 'fa-map-signs'}}" heading="{{connectivityStatusParams.heading}}" large="no" closeable="yes">
+ <div class="modal-body" ng-switch="connectivityStatusParams.type">
+ <div ng-switch-when="listeners">
+ <p translate ng-if="listenersRunning.length == 0">
+ Syncthing is not listening for connection attempts from other devices on any address. Only outgoing connections from this device may work.
+ </p>
+ <div ng-if="listenersRunning.length > 0">
+ <p translate>
+ Syncthing is listening on the following network addresses for connection attempts from other devices:
+ </p>
+ <ul>
+ <li ng-repeat="listener in listenersRunning">{{listener}}</li>
+ </ul>
+ </div>
+ <div ng-if="listenersFailed.length > 0">
+ <p translate>
+ Some listening addresses could not be enabled to accept connections:
+ </p>
+ <ul>
+ <li ng-repeat="listener in listenersFailed">{{listener}}</li>
+ </ul>
+ </div>
+ </div>
+ <div ng-switch-default><!-- discovery methods -->
+ <p translate ng-if="discoveryRunning.length == 0">
+ This device cannot automatically discover other devices or announce its own address to be found by others. Only devices with statically configured addresses can connect.
+ </p>
+ <div ng-if="discoveryRunning.length > 0">
+ <p translate>
+ The following methods are used to discover other devices on the network and announce this device to be found by others:
+ </p>
+ <ul>
+ <li ng-repeat="discovery in discoveryRunning">{{discovery}}</li>
+ </ul>
+ </div>
+ <div ng-if="discoveryFailed.length > 0">
+ <p translate>
+ Some discovery methods could not be established for finding other devices or announcing this device:
+ </p>
+ <ul>
+ <li ng-repeat="discovery in discoveryFailed">{{discovery}}</li>
+ </ul>
+ </div>
+ <div class="row">
+ <div class="col-md-offset-2 col-md-8">
+ <div class="panel panel-default">
+ <div translate class="panel-body">
+ Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-default btn-sm" data-dismiss="modal">
+ <span class="fas fa-times"></span>&nbsp;<span translate>Close</span>
+ </button>
+ </div>
+</modal>
diff --git a/gui/default/syncthing/core/discoveryFailuresModalView.html b/gui/default/syncthing/core/discoveryFailuresModalView.html
deleted file mode 100644
index 9c8b19b082..0000000000
--- a/gui/default/syncthing/core/discoveryFailuresModalView.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<modal id="discovery-failures" status="danger" icon="fas fa-exclamation-circle" heading="{{'Discovery Failures' | translate}}" large="yes" closeable="yes">
- <div class="modal-body">
- <ul>
- <li ng-repeat="failure in discoveryFailed">{{failure}}</li>
- </ul>
- </div>
- <div class="row">
- <div class="col-md-offset-2 col-md-8">
- <div class="panel panel-default">
- <div translate class="panel-body">
- Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.
- </div>
- </div>
- </div>
- </div>
- <div class="modal-footer">
- <button type="button" class="btn btn-default btn-sm" data-dismiss="modal">
- <span class="fas fa-times"></span>&nbsp;<span translate>Close</span>
- </button>
- </div>
-</modal>
diff --git a/gui/default/syncthing/core/syncthingController.js b/gui/default/syncthing/core/syncthingController.js
index ababd2c79b..775200d118 100755
--- a/gui/default/syncthing/core/syncthingController.js
+++ b/gui/default/syncthing/core/syncthingController.js
@@ -471,22 +471,30 @@ angular.module('syncthing.core')
}
var listenersFailed = [];
+ var listenersRunning = [];
for (var address in data.connectionServiceStatus) {
if (data.connectionServiceStatus[address].error) {
listenersFailed.push(address + ": " + data.connectionServiceStatus[address].error);
+ } else {
+ listenersRunning.push(address);
}
}
$scope.listenersFailed = listenersFailed;
+ $scope.listenersRunning = listenersRunning;
$scope.listenersTotal = $scope.sizeOf(data.connectionServiceStatus);
- $scope.discoveryTotal = data.discoveryMethods;
var discoveryFailed = [];
- for (var disco in data.discoveryErrors) {
- if (data.discoveryErrors[disco]) {
- discoveryFailed.push(disco + ": " + data.discoveryErrors[disco]);
+ var discoveryRunning = [];
+ for (var disco in data.discoveryStatus) {
+ if (data.discoveryStatus[disco] && data.discoveryStatus[disco].error) {
+ discoveryFailed.push(disco + ": " + data.discoveryStatus[disco].error);
+ } else {
+ discoveryRunning.push(disco);
}
}
$scope.discoveryFailed = discoveryFailed;
+ $scope.discoveryRunning = discoveryRunning;
+ $scope.discoveryTotal = $scope.sizeOf(data.discoveryStatus);
refreshNoAuthWarning();
@@ -1249,8 +1257,34 @@ angular.module('syncthing.core')
}
};
- $scope.showDiscoveryFailures = function () {
- $('#discovery-failures').modal();
+ $scope.showListenerStatus = function () {
+ var params = {
+ type: 'listeners',
+ };
+ if ($scope.listenersFailed.length > 0) {
+ params.status = 'danger';
+ params.heading = $translate.instant("Listener Failures");
+ } else {
+ params.status = 'default';
+ params.heading = $translate.instant("Listener Status");
+ }
+ $scope.connectivityStatusParams = params;
+ $('#connectivity-status').modal();
+ };
+
+ $scope.showDiscoveryStatus = function () {
+ var params = {
+ type: 'discovery',
+ };
+ if ($scope.discoveryFailed.length > 0) {
+ params.status = 'danger';
+ params.heading = $translate.instant("Discovery Failures");
+ } else {
+ params.status = 'default';
+ params.heading = $translate.instant("Discovery Status");
+ }
+ $scope.connectivityStatusParams = params;
+ $('#connectivity-status').modal();
};
$scope.logging = {
diff --git a/lib/api/api.go b/lib/api/api.go
index 0b59b27b91..af685af4fc 100644
--- a/lib/api/api.go
+++ b/lib/api/api.go
@@ -1023,16 +1023,16 @@ func (s *service) getSystemStatus(w http.ResponseWriter, r *http.Request) {
res["tilde"] = tilde
if s.cfg.Options().LocalAnnEnabled || s.cfg.Options().GlobalAnnEnabled {
res["discoveryEnabled"] = true
- discoErrors := make(map[string]string)
- discoMethods := 0
- for disco, err := range s.discoverer.ChildErrors() {
- discoMethods++
- if err != nil {
- discoErrors[disco] = err.Error()
+ discoStatus := s.discoverer.ChildErrors()
+ res["discoveryStatus"] = discoveryStatusMap(discoStatus)
+ res["discoveryMethods"] = len(discoStatus) // DEPRECATED: Redundant, only for backwards compatibility, should be removed.
+ discoErrors := make(map[string]*string, len(discoStatus))
+ for s, e := range discoStatus {
+ if e != nil {
+ discoErrors[s] = errorString(e)
}
}
- res["discoveryMethods"] = discoMethods
- res["discoveryErrors"] = discoErrors
+ res["discoveryErrors"] = discoErrors // DEPRECATED: Redundant, only for backwards compatibility, should be removed.
}
res["connectionServiceStatus"] = s.connectionsService.ListenerStatus()
@@ -1905,6 +1905,20 @@ func errorString(err error) *string {
return nil
}
+type discoveryStatusEntry struct {
+ Error *string `json:"error"`
+}
+
+func discoveryStatusMap(errs map[string]error) map[string]discoveryStatusEntry {
+ out := make(map[string]discoveryStatusEntry, len(errs))
+ for s, e := range errs {
+ out[s] = discoveryStatusEntry{
+ Error: errorString(e),
+ }
+ }
+ return out
+}
+
// sanitizedHostname returns the given name in a suitable form for use as
// the common name in a certificate, or an error.
func sanitizedHostname(name string) (string, error) {