From ea0a408849668e3ea1abd1f900ff4c2d1ba2b269 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Colomb?= Date: Mon, 7 Jun 2021 09:08:44 +0200 Subject: [PATCH] gui: Modal dialog for listeners and discovery status (#7539) --- gui/default/assets/lang/lang-en.json | 8 +++ gui/default/index.html | 24 +++----- .../core/connectivityStatusModalView.html | 60 +++++++++++++++++++ .../core/discoveryFailuresModalView.html | 21 ------- .../syncthing/core/syncthingController.js | 46 ++++++++++++-- lib/api/api.go | 30 +++++++--- 6 files changed, 139 insertions(+), 50 deletions(-) create mode 100644 gui/default/syncthing/core/connectivityStatusModalView.html delete mode 100644 gui/default/syncthing/core/discoveryFailuresModalView.html diff --git a/gui/default/assets/lang/lang-en.json b/gui/default/assets/lang/lang-en.json index 8fbbf983f..34c2e5c45 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 9758f3763..2c42d3326 100644 --- a/gui/default/index.html +++ b/gui/default/index.html @@ -655,26 +655,20 @@  Listeners - - {{listenersTotal}}/{{listenersTotal}} - - - - {{listenersTotal-listenersFailed.length}}/{{listenersTotal}} - + + + {{listenersTotal-listenersFailed.length}}/{{listenersTotal}} +  Discovery - - {{discoveryTotal}}/{{discoveryTotal}} - - - - {{discoveryTotal-discoveryFailed.length}}/{{discoveryTotal}} - + + + {{discoveryTotal-discoveryFailed.length}}/{{discoveryTotal}} + @@ -921,7 +915,7 @@ - + diff --git a/gui/default/syncthing/core/connectivityStatusModalView.html b/gui/default/syncthing/core/connectivityStatusModalView.html new file mode 100644 index 000000000..95c39c9d0 --- /dev/null +++ b/gui/default/syncthing/core/connectivityStatusModalView.html @@ -0,0 +1,60 @@ + + + + diff --git a/gui/default/syncthing/core/discoveryFailuresModalView.html b/gui/default/syncthing/core/discoveryFailuresModalView.html deleted file mode 100644 index 9c8b19b08..000000000 --- a/gui/default/syncthing/core/discoveryFailuresModalView.html +++ /dev/null @@ -1,21 +0,0 @@ - - -
-
-
-
- Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity. -
-
-
-
- -
diff --git a/gui/default/syncthing/core/syncthingController.js b/gui/default/syncthing/core/syncthingController.js index ababd2c79..775200d11 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 0b59b27b9..af685af4f 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) {