mirror of
https://github.com/syncthing/syncthing.git
synced 2026-01-19 11:18:13 -05:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2470875d14 | ||
|
|
930e90289f | ||
|
|
bf959a77e2 | ||
|
|
3cc4cb0a0b | ||
|
|
e6cba61740 | ||
|
|
cd7ce73f59 | ||
|
|
fab4e33c58 | ||
|
|
b79b13a75b | ||
|
|
c294d5f087 | ||
|
|
10ead2e61f |
@@ -618,7 +618,7 @@ func restGetUpgrade(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, upgrade.ErrUpgradeUnsupported.Error(), 500)
|
||||
return
|
||||
}
|
||||
rel, err := upgrade.LatestRelease(strings.Contains(Version, "-beta"))
|
||||
rel, err := upgrade.LatestRelease(Version)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
@@ -626,7 +626,8 @@ func restGetUpgrade(w http.ResponseWriter, r *http.Request) {
|
||||
res := make(map[string]interface{})
|
||||
res["running"] = Version
|
||||
res["latest"] = rel.Tag
|
||||
res["newer"] = upgrade.CompareVersions(rel.Tag, Version) == 1
|
||||
res["newer"] = upgrade.CompareVersions(rel.Tag, Version) == upgrade.Newer
|
||||
res["majorNewer"] = upgrade.CompareVersions(rel.Tag, Version) == upgrade.MajorNewer
|
||||
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
json.NewEncoder(w).Encode(res)
|
||||
@@ -660,14 +661,14 @@ func restGetLang(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func restPostUpgrade(w http.ResponseWriter, r *http.Request) {
|
||||
rel, err := upgrade.LatestRelease(strings.Contains(Version, "-beta"))
|
||||
rel, err := upgrade.LatestRelease(Version)
|
||||
if err != nil {
|
||||
l.Warnln("getting latest release:", err)
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
}
|
||||
|
||||
if upgrade.CompareVersions(rel.Tag, Version) == 1 {
|
||||
if upgrade.CompareVersions(rel.Tag, Version) > upgrade.Equal {
|
||||
err = upgrade.To(rel)
|
||||
if err != nil {
|
||||
l.Warnln("upgrading:", err)
|
||||
@@ -828,6 +829,8 @@ func mimeTypeForFile(file string) string {
|
||||
return "application/x-font-ttf"
|
||||
case ".woff":
|
||||
return "application/x-font-woff"
|
||||
case ".svg":
|
||||
return "image/svg+xml"
|
||||
default:
|
||||
return mime.TypeByExtension(ext)
|
||||
}
|
||||
|
||||
@@ -324,7 +324,7 @@ func main() {
|
||||
}
|
||||
|
||||
if doUpgrade || doUpgradeCheck {
|
||||
rel, err := upgrade.LatestRelease(IsBeta)
|
||||
rel, err := upgrade.LatestRelease(Version)
|
||||
if err != nil {
|
||||
l.Fatalln("Upgrade:", err) // exits 1
|
||||
}
|
||||
@@ -1057,7 +1057,7 @@ func autoUpgrade() {
|
||||
case <-timer.C:
|
||||
}
|
||||
|
||||
rel, err := upgrade.LatestRelease(IsBeta)
|
||||
rel, err := upgrade.LatestRelease(Version)
|
||||
if err == upgrade.ErrUpgradeUnsupported {
|
||||
events.Default.Unsubscribe(sub)
|
||||
return
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"A new major version may not be compatible with previous versions.": "A new major version may not be compatible with previous versions.",
|
||||
"API Key": "API Key",
|
||||
"About": "About",
|
||||
"Add": "Add",
|
||||
@@ -72,6 +73,7 @@
|
||||
"Latest Release": "Latest Release",
|
||||
"Local Discovery": "Local Discovery",
|
||||
"Local State": "Local State",
|
||||
"Major Upgrade": "Major Upgrade",
|
||||
"Maximum Age": "Maximum Age",
|
||||
"Metadata Only": "Metadata Only",
|
||||
"Move to top of queue": "Move to top of queue",
|
||||
@@ -92,11 +94,13 @@
|
||||
"Override Changes": "Override Changes",
|
||||
"Path to the folder on the local computer. Will be created if it does not exist. The tilde character (~) can be used as a shortcut for": "Path to the folder on the local computer. Will be created if it does not exist. The tilde character (~) can be used as a shortcut for",
|
||||
"Path where versions should be stored (leave empty for the default .stversions folder in the folder).": "Path where versions should be stored (leave empty for the default .stversions folder in the folder).",
|
||||
"Please consult the release notes before performing a major upgrade.": "Please consult the release notes before performing a major upgrade.",
|
||||
"Please wait": "Please wait",
|
||||
"Preview": "Preview",
|
||||
"Preview Usage Report": "Preview Usage Report",
|
||||
"Quick guide to supported patterns": "Quick guide to supported patterns",
|
||||
"RAM Utilization": "RAM Utilization",
|
||||
"Release Notes": "Release Notes",
|
||||
"Rescan": "Rescan",
|
||||
"Rescan All": "Rescan All",
|
||||
"Rescan Interval": "Rescan Interval",
|
||||
@@ -155,10 +159,12 @@
|
||||
"The number of versions must be a number and cannot be blank.": "The number of versions must be a number and cannot be blank.",
|
||||
"The rescan interval must be a non-negative number of seconds.": "The rescan interval must be a non-negative number of seconds.",
|
||||
"The rescan interval must be at least 5 seconds.": "The rescan interval must be at least 5 seconds.",
|
||||
"This is a major version upgrade.": "This is a major version upgrade.",
|
||||
"Unknown": "Unknown",
|
||||
"Unshared": "Unshared",
|
||||
"Unused": "Unused",
|
||||
"Up to Date": "Up to Date",
|
||||
"Upgrade": "Upgrade",
|
||||
"Upgrade To {%version%}": "Upgrade To {{version}}",
|
||||
"Upgrading": "Upgrading",
|
||||
"Upload Rate": "Upload Rate",
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"Add new folder?": "Aggiungere una nuova cartella?",
|
||||
"Address": "Indirizzo",
|
||||
"Addresses": "Indirizzi",
|
||||
"All Data": "All Data",
|
||||
"All Data": "Tutti i dati",
|
||||
"Allow Anonymous Usage Reporting?": "Abilitare Statistiche Anonime di Utilizzo?",
|
||||
"Anonymous Usage Reporting": "Statistiche Anonime di Utilizzo",
|
||||
"Any devices configured on an introducer device will be added to this device as well.": "Qualsiasi dispositivo configurato in un introduttore verrà aggiunto anche a questo dispositivo.",
|
||||
@@ -17,7 +17,7 @@
|
||||
"Changelog": "Changelog",
|
||||
"Close": "Chiudi",
|
||||
"Comment, when used at the start of a line": "Per commentare, va inserito all'inizio di una riga",
|
||||
"Compression": "Compression",
|
||||
"Compression": "Compressione",
|
||||
"Compression is recommended in most setups.": "La compressione è raccomandata nella maggior parte delle configurazioni.",
|
||||
"Connection Error": "Errore di Connessione",
|
||||
"Copied from elsewhere": "Copiato da qualche altra parte",
|
||||
@@ -73,8 +73,8 @@
|
||||
"Local Discovery": "Individuazione Locale",
|
||||
"Local State": "Stato Locale",
|
||||
"Maximum Age": "Durata Massima",
|
||||
"Metadata Only": "Metadata Only",
|
||||
"Move to top of queue": "Move to top of queue",
|
||||
"Metadata Only": "Solo i metadati",
|
||||
"Move to top of queue": "Posiziona in cima alla coda",
|
||||
"Multi level wildcard (matches multiple directory levels)": "Metacarattere multi-livello (corrisponde alle cartelle e alle sotto-cartelle)",
|
||||
"Never": "Mai",
|
||||
"New Device": "Nuovo Dispositivo",
|
||||
@@ -98,7 +98,7 @@
|
||||
"Quick guide to supported patterns": "Guida veloce agli schemi supportati",
|
||||
"RAM Utilization": "Utilizzo RAM",
|
||||
"Rescan": "Riscansiona",
|
||||
"Rescan All": "Rescan All",
|
||||
"Rescan All": "Riscansiona Tutto",
|
||||
"Rescan Interval": "Intervallo Scansione",
|
||||
"Restart": "Riavvia",
|
||||
"Restart Needed": "Riavvio Necessario",
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"Add new folder?": "Dodać nowy folder?",
|
||||
"Address": "Adres",
|
||||
"Addresses": "Adresy",
|
||||
"All Data": "All Data",
|
||||
"All Data": "Wszystkie dane",
|
||||
"Allow Anonymous Usage Reporting?": "Zezwalaj na anonimowe statystyki użycia",
|
||||
"Anonymous Usage Reporting": "Anonimowe statystyki użycia",
|
||||
"Any devices configured on an introducer device will be added to this device as well.": "Wszystkie urządzenia skonfigurowane na urządzeniu wprowadzającym zostaną dodane także do tego urządzenia.",
|
||||
@@ -17,17 +17,17 @@
|
||||
"Changelog": "Historia zmian",
|
||||
"Close": "Zamknij",
|
||||
"Comment, when used at the start of a line": "Komentarz, jeżeli użyty na początku linii",
|
||||
"Compression": "Compression",
|
||||
"Compression": "Kompresja",
|
||||
"Compression is recommended in most setups.": "Kompresja jest zalecana w większości przypadków",
|
||||
"Connection Error": "Błąd połączenia",
|
||||
"Copied from elsewhere": "Copied from elsewhere",
|
||||
"Copied from original": "Copied from original",
|
||||
"Copied from original": "Skopiowane z oryginału",
|
||||
"Copyright © 2014 Jakob Borg and the following Contributors:": "Copyright © 2014 Jakob Borg i następujący współautorzy:",
|
||||
"Delete": "Usuń",
|
||||
"Device ID": "ID urządzenia",
|
||||
"Device Identification": "Identyfikator urządzenia",
|
||||
"Device Name": "Nazwa urządzenia",
|
||||
"Device {%device%} ({%address%}) wants to connect. Add new device?": "Device {{device}} ({{address}}) wants to connect. Add new device?",
|
||||
"Device {%device%} ({%address%}) wants to connect. Add new device?": "Urządzenie {{device}} ({{address}}) chce się połączyć. Zezwolić?",
|
||||
"Devices": "Urządzenia",
|
||||
"Disconnected": "Rozłączony",
|
||||
"Documentation": "Dokumentacja",
|
||||
@@ -58,7 +58,7 @@
|
||||
"Global Discovery Server": "Globalny serwer rozgłoszeniowy",
|
||||
"Global State": "Status globalny",
|
||||
"Idle": "Bezczynny",
|
||||
"Ignore": "Ignore",
|
||||
"Ignore": "Ignoruj",
|
||||
"Ignore Patterns": "Wzorce ignorowania",
|
||||
"Ignore Permissions": "Ignoruj uprawnienia",
|
||||
"Incoming Rate Limit (KiB/s)": "Ograniczenie prędkości odbierania (KiB/s)",
|
||||
@@ -68,13 +68,13 @@
|
||||
"Last File Received": "Ostatni otrzymany plik",
|
||||
"Last File Synced": "Ostatni zsynchronizowany plik",
|
||||
"Last seen": "Ostatnio widziany",
|
||||
"Later": "Later",
|
||||
"Later": "Później",
|
||||
"Latest Release": "Najnowsza wersja",
|
||||
"Local Discovery": "Lokalne odnajdywanie",
|
||||
"Local State": "Status lokalny",
|
||||
"Maximum Age": "Maksymalny wiek",
|
||||
"Metadata Only": "Metadata Only",
|
||||
"Move to top of queue": "Move to top of queue",
|
||||
"Metadata Only": "Tylko metadane",
|
||||
"Move to top of queue": "Przenieś na początek kolejki",
|
||||
"Multi level wildcard (matches multiple directory levels)": "Wieloznaczność na poziomie katalogów i plików (uwzględnia nazwy folderów i plików)",
|
||||
"Never": "Nigdy",
|
||||
"New Device": "Nowe urządzenie",
|
||||
@@ -83,11 +83,11 @@
|
||||
"No File Versioning": "Bez wersjonowania pliku",
|
||||
"Notice": "Wskazówka",
|
||||
"OK": "OK",
|
||||
"Off": "Off",
|
||||
"Off": "Wyłącz",
|
||||
"Offline": "Rozłączony",
|
||||
"Online": "Połączony",
|
||||
"Out Of Sync": "Niezsynchronizowane",
|
||||
"Out of Sync Items": "Out of Sync Items",
|
||||
"Out of Sync Items": "Niezsynchronizowane pliki",
|
||||
"Outgoing Rate Limit (KiB/s)": "Ograniczenie prędkości wysyłania (KiB/s)",
|
||||
"Override Changes": "Nadpisz zmiany",
|
||||
"Path to the folder on the local computer. Will be created if it does not exist. The tilde character (~) can be used as a shortcut for": "Ścieżka do lokalnego folderu. Zostanie utworzona jeżeli nie istnieje.\nZnak tyldy (~) może zostać użyty jako skrót do",
|
||||
@@ -98,29 +98,29 @@
|
||||
"Quick guide to supported patterns": "Krótki przewodnik po obsługiwanych wzorcach",
|
||||
"RAM Utilization": "Użycie pamięci RAM",
|
||||
"Rescan": "Skanuj ponownie",
|
||||
"Rescan All": "Rescan All",
|
||||
"Rescan All": "Skanuj wszystko ponownie",
|
||||
"Rescan Interval": "Interwał skanowania",
|
||||
"Restart": "Uruchom ponownie",
|
||||
"Restart Needed": "Wymagane ponowne uruchomienie",
|
||||
"Restarting": "Uruchamianie ponowne",
|
||||
"Reused": "Reused",
|
||||
"Reused": "Ponownie użyte",
|
||||
"Save": "Zapisz",
|
||||
"Scanning": "Skanowanie",
|
||||
"Select the devices to share this folder with.": "Wybierz urządzenie, któremu udostępnić folder.",
|
||||
"Select the folders to share with this device.": "Wybierz foldery do współdzielenia z tym urządzeniem.",
|
||||
"Settings": "Ustawienia",
|
||||
"Share": "Share",
|
||||
"Share Folder": "Share Folder",
|
||||
"Share Folders With Device": "Share Folders With Device",
|
||||
"Share": "Udostępnij",
|
||||
"Share Folder": "Udostępnij folder",
|
||||
"Share Folders With Device": "Udostępnij foldery między urządzeniami",
|
||||
"Share With Devices": "Udostępnij dla urządzenia",
|
||||
"Share this folder?": "Share this folder?",
|
||||
"Share this folder?": "Udostępnić ten folder?",
|
||||
"Shared With": "Współdzielony z",
|
||||
"Short identifier for the folder. Must be the same on all cluster devices.": "Krótki identyfikator folderu. Musi być taki sam na wszystkich urządzeniach.",
|
||||
"Show ID": "Pokaż ID",
|
||||
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Pokazane w statusie zamiast ID urządzenia.Zostanie wysłane do innych urządzeń jako opcjonalna domyślna nazwa.",
|
||||
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Pokazane w statusie zamiast ID urządzenia. Zostanie zaktualizowane do nazwy urządzenia jeżeli pozostanie puste.",
|
||||
"Shutdown": "Wyłącz",
|
||||
"Shutdown Complete": "Shutdown Complete",
|
||||
"Shutdown Complete": "Wyłączanie ukończone",
|
||||
"Simple File Versioning": "Proste wersjonowanie pliku",
|
||||
"Single level wildcard (matches within a directory only)": "Wieloznaczność na poziomie plików (uwzględnia nazwy plików)",
|
||||
"Source Code": "Kod źródłowy",
|
||||
@@ -137,7 +137,7 @@
|
||||
"Syncthing is restarting.": "Restart Syncthing",
|
||||
"Syncthing is upgrading.": "Aktualizowanie Syncthing",
|
||||
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing wydaje się być wyłączony lub jest problem z twoim połączeniem internetowym. Próbuje ponownie...",
|
||||
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing seems to be experiencing a problem processing your request. Please reload your browser or restart Syncthing if the problem persists.",
|
||||
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing nie może przetworzyć twojego zapytania. Proszę przeładuj stronę lub restartuj Syncthing gdy problem pozostanie.",
|
||||
"The aggregated statistics are publicly available at {%url%}.": "Zebrane statystyki są publicznie dostępna pod adresem {{url}}.",
|
||||
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "Konfiguracja została zapisana lecz nie jest aktywna. Syncthing musi zostać zrestartowany aby aktywować nową konfiguracje.",
|
||||
"The device ID cannot be blank.": "ID urządzenia nie może być puste.",
|
||||
@@ -156,7 +156,7 @@
|
||||
"The rescan interval must be a non-negative number of seconds.": "Interwał skanowania musi być niezerową liczbą sekund.",
|
||||
"The rescan interval must be at least 5 seconds.": "Interwał skanowania musi wynosić co najmniej 5 sekund.",
|
||||
"Unknown": "Nieznany",
|
||||
"Unshared": "Unshared",
|
||||
"Unshared": "Nieudostępnione",
|
||||
"Unused": "Nieużywane",
|
||||
"Up to Date": "Aktualny",
|
||||
"Upgrade To {%version%}": "Aktualizuj do {{version}}",
|
||||
@@ -173,5 +173,5 @@
|
||||
"You must keep at least one version.": "Musisz posiadać przynajmniej jedną wersję",
|
||||
"full documentation": "pełna dokumentacja",
|
||||
"items": "pozycji",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} wants to share folder \"{{folder}}\"."
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} chce udostępnić folder \"{{folder}}\""
|
||||
}
|
||||
@@ -82,7 +82,7 @@
|
||||
"No": "Ні",
|
||||
"No File Versioning": "Версіонування вимкнено",
|
||||
"Notice": "Повідомлення",
|
||||
"OK": "OK",
|
||||
"OK": "Гаразд",
|
||||
"Off": "Вимкнути",
|
||||
"Offline": "Офлайн",
|
||||
"Online": "Онлайн",
|
||||
@@ -106,8 +106,8 @@
|
||||
"Reused": "Використано вдруге",
|
||||
"Save": "Зберегти",
|
||||
"Scanning": "Сканування",
|
||||
"Select the devices to share this folder with.": "Оберіть пристрої із якими обміняти дану директорію.",
|
||||
"Select the folders to share with this device.": "Оберіть директорії які обмінювати із цим пристроєм.",
|
||||
"Select the devices to share this folder with.": "Оберіть пристрої, які матимуть доступ до цієї директорії.",
|
||||
"Select the folders to share with this device.": "Оберіть директорії до яких матиме доступ цей пристрій.",
|
||||
"Settings": "Налаштування",
|
||||
"Share": "Розповсюдити ",
|
||||
"Share Folder": "Розповсюдити каталог",
|
||||
|
||||
@@ -38,6 +38,12 @@
|
||||
<span translate translate-value-version="{{upgradeInfo.latest}}">Upgrade To {%version%}</span>
|
||||
</button>
|
||||
</li>
|
||||
<li ng-if="upgradeInfo && upgradeInfo.majorNewer">
|
||||
<button type="button" class="btn navbar-btn btn-danger btn-sm" href="" ng-click="upgradeMajor()">
|
||||
<span class="glyphicon glyphicon-chevron-up"></span> 
|
||||
<span translate translate-value-version="{{upgradeInfo.latest}}">Upgrade To {%version%}</span>
|
||||
</button>
|
||||
</li>
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><span class="glyphicon glyphicon-cog" aria-label="Edit"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
@@ -460,6 +466,35 @@
|
||||
<img ng-if="myID" class="center-block img-thumbnail" src="qr/?text={{myID}}"/>
|
||||
</modal>
|
||||
|
||||
<!-- Major upgrade modal -->
|
||||
|
||||
<div id="majorUpgrade" class="modal fade" tabindex="-1" data-backdrop="true" data-keyboard="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header alert alert-danger">
|
||||
<h4 class="modal-title">
|
||||
<span ng-if="icon" class="glyphicon glyphicon-chevron-up"></span>
|
||||
<span translate>Major Upgrade</span>
|
||||
</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>
|
||||
<span translate>This is a major version upgrade.</span>
|
||||
<span translate>A new major version may not be compatible with previous versions.</span>
|
||||
<span translate>Please consult the release notes before performing a major upgrade.</span>
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://github.com/syncthing/syncthing/releases/latest" target="_blank" translate>Release Notes</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary btn-sm" ng-click="upgrade()"><span class="glyphicon glyphicon-ok"></span> <span translate>Upgrade</span></button>
|
||||
<button type="button" class="btn btn-default btn-sm" data-dismiss="modal"><span class="glyphicon glyphicon-remove"></span> <span translate>Close</span></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Device editor modal -->
|
||||
|
||||
<div id="editDevice" class="modal fade" tabindex="-1">
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
angular.module('syncthing.core')
|
||||
.config(function($locationProvider) {
|
||||
$locationProvider.html5Mode(true).hashPrefix('!');
|
||||
})
|
||||
.controller('SyncthingController', function ($scope, $http, $location, LocaleService) {
|
||||
'use strict';
|
||||
|
||||
@@ -669,6 +672,7 @@ angular.module('syncthing.core')
|
||||
|
||||
$scope.upgrade = function () {
|
||||
restarting = true;
|
||||
$('#majorUpgrade').modal('hide');
|
||||
$('#upgrading').modal();
|
||||
$http.post(urlbase + '/upgrade').success(function () {
|
||||
$('#restarting').modal();
|
||||
@@ -678,6 +682,10 @@ angular.module('syncthing.core')
|
||||
});
|
||||
};
|
||||
|
||||
$scope.upgradeMajor = function () {
|
||||
$('#majorUpgrade').modal();
|
||||
};
|
||||
|
||||
$scope.shutdown = function () {
|
||||
restarting = true;
|
||||
$http.post(urlbase + '/shutdown').success(function () {
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -370,6 +370,11 @@ func (cfg *Configuration) prepare(myID protocol.DeviceID) {
|
||||
}
|
||||
}
|
||||
|
||||
// Very short reconnection intervals are annoying
|
||||
if cfg.Options.ReconnectIntervalS < 5 {
|
||||
cfg.Options.ReconnectIntervalS = 5
|
||||
}
|
||||
|
||||
cfg.Options.ListenAddress = uniqueStrings(cfg.Options.ListenAddress)
|
||||
cfg.Options.GlobalAnnServers = uniqueStrings(cfg.Options.GlobalAnnServers)
|
||||
|
||||
|
||||
@@ -27,14 +27,17 @@ type Discoverer struct {
|
||||
localBcastIntv time.Duration
|
||||
localBcastStart time.Time
|
||||
cacheLifetime time.Duration
|
||||
negCacheCutoff time.Duration
|
||||
broadcastBeacon beacon.Interface
|
||||
multicastBeacon beacon.Interface
|
||||
registry map[protocol.DeviceID][]CacheEntry
|
||||
registryLock sync.RWMutex
|
||||
extPort uint16
|
||||
localBcastTick <-chan time.Time
|
||||
forcedBcastTick chan time.Time
|
||||
|
||||
registryLock sync.RWMutex
|
||||
registry map[protocol.DeviceID][]CacheEntry
|
||||
lastLookup map[protocol.DeviceID]time.Time
|
||||
|
||||
clients []Client
|
||||
mut sync.RWMutex
|
||||
}
|
||||
@@ -54,7 +57,9 @@ func NewDiscoverer(id protocol.DeviceID, addresses []string) *Discoverer {
|
||||
listenAddrs: addresses,
|
||||
localBcastIntv: 30 * time.Second,
|
||||
cacheLifetime: 5 * time.Minute,
|
||||
negCacheCutoff: 3 * time.Minute,
|
||||
registry: make(map[protocol.DeviceID][]CacheEntry),
|
||||
lastLookup: make(map[protocol.DeviceID]time.Time),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,18 +160,28 @@ func (d *Discoverer) ExtAnnounceOK() map[string]bool {
|
||||
func (d *Discoverer) Lookup(device protocol.DeviceID) []string {
|
||||
d.registryLock.RLock()
|
||||
cached := d.filterCached(d.registry[device])
|
||||
lastLookup := d.lastLookup[device]
|
||||
d.registryLock.RUnlock()
|
||||
|
||||
d.mut.RLock()
|
||||
defer d.mut.RUnlock()
|
||||
|
||||
var addrs []string
|
||||
if len(cached) > 0 {
|
||||
addrs = make([]string, len(cached))
|
||||
// There are cached address entries.
|
||||
addrs := make([]string, len(cached))
|
||||
for i := range cached {
|
||||
addrs[i] = cached[i].Address
|
||||
}
|
||||
} else if len(d.clients) != 0 && time.Since(d.localBcastStart) > d.localBcastIntv {
|
||||
return addrs
|
||||
}
|
||||
|
||||
if time.Since(lastLookup) < d.negCacheCutoff {
|
||||
// We have recently tried to lookup this address and failed. Lets
|
||||
// chill for a while.
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(d.clients) != 0 && time.Since(d.localBcastStart) > d.localBcastIntv {
|
||||
// Only perform external lookups if we have at least one external
|
||||
// server client and one local announcement interval has passed. This is
|
||||
// to avoid finding local peers on their remote address at startup.
|
||||
@@ -187,6 +202,7 @@ func (d *Discoverer) Lookup(device protocol.DeviceID) []string {
|
||||
seen := make(map[string]struct{})
|
||||
now := time.Now()
|
||||
|
||||
var addrs []string
|
||||
for result := range results {
|
||||
for _, addr := range result {
|
||||
_, ok := seen[addr]
|
||||
@@ -203,9 +219,13 @@ func (d *Discoverer) Lookup(device protocol.DeviceID) []string {
|
||||
|
||||
d.registryLock.Lock()
|
||||
d.registry[device] = cached
|
||||
d.lastLookup[device] = time.Now()
|
||||
d.registryLock.Unlock()
|
||||
|
||||
return addrs
|
||||
}
|
||||
return addrs
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Discoverer) Hint(device string, addrs []string) {
|
||||
|
||||
@@ -489,7 +489,11 @@ func (p *rwFolder) handleDir(file protocol.FileInfo) {
|
||||
// we can pass it to InWritableDir. We use a regular Mkdir and
|
||||
// not MkdirAll because the parent should already exist.
|
||||
mkdir := func(path string) error {
|
||||
return os.Mkdir(path, mode)
|
||||
err = os.Mkdir(path, mode)
|
||||
if err != nil || p.ignorePerms {
|
||||
return err
|
||||
}
|
||||
return os.Chmod(path, mode)
|
||||
}
|
||||
|
||||
if err = osutil.InWritableDir(mkdir, realName); err == nil {
|
||||
|
||||
@@ -111,7 +111,8 @@ func (w *Walker) walkAndHashFiles(fchan chan protocol.FileInfo) filepath.WalkFun
|
||||
// Return value used when we are returning early and don't want to
|
||||
// process the item. For directories, this means do-not-descend.
|
||||
var skip error // nil
|
||||
if info.IsDir() {
|
||||
// info nil when error is not nil
|
||||
if info != nil && info.IsDir() {
|
||||
skip = filepath.SkipDir
|
||||
}
|
||||
|
||||
|
||||
@@ -259,6 +259,14 @@ func TestNormalization(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue1507(t *testing.T) {
|
||||
w := Walker{}
|
||||
c := make(chan protocol.FileInfo, 100)
|
||||
fn := w.walkAndHashFiles(c)
|
||||
|
||||
fn("", nil, protocol.ErrClosed)
|
||||
}
|
||||
|
||||
func walkDir(dir string) ([]protocol.FileInfo, error) {
|
||||
w := Walker{
|
||||
Dir: dir,
|
||||
|
||||
3814
internal/upgrade/testdata/github-releases.json
vendored
Normal file
3814
internal/upgrade/testdata/github-releases.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -23,36 +23,70 @@ import (
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Returns the latest release, including prereleases or not depending on the argument
|
||||
func LatestRelease(prerelease bool) (Release, error) {
|
||||
resp, err := http.Get("https://api.github.com/repos/syncthing/syncthing/releases?per_page=10")
|
||||
// Returns the latest releases, including prereleases or not depending on the argument
|
||||
func LatestGithubReleases(version string) ([]Release, error) {
|
||||
resp, err := http.Get("https://api.github.com/repos/syncthing/syncthing/releases?per_page=30")
|
||||
if err != nil {
|
||||
return Release{}, err
|
||||
return nil, err
|
||||
}
|
||||
if resp.StatusCode > 299 {
|
||||
return Release{}, fmt.Errorf("API call returned HTTP error: %s", resp.Status)
|
||||
return nil, fmt.Errorf("API call returned HTTP error: %s", resp.Status)
|
||||
}
|
||||
|
||||
var rels []Release
|
||||
json.NewDecoder(resp.Body).Decode(&rels)
|
||||
resp.Body.Close()
|
||||
|
||||
return rels, nil
|
||||
}
|
||||
|
||||
type SortByRelease []Release
|
||||
|
||||
func (s SortByRelease) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
func (s SortByRelease) Swap(i, j int) {
|
||||
s[i], s[j] = s[j], s[i]
|
||||
}
|
||||
func (s SortByRelease) Less(i, j int) bool {
|
||||
return CompareVersions(s[i].Tag, s[j].Tag) > 0
|
||||
}
|
||||
|
||||
func LatestRelease(version string) (Release, error) {
|
||||
rels, _ := LatestGithubReleases(version)
|
||||
return SelectLatestRelease(version, rels)
|
||||
}
|
||||
|
||||
func SelectLatestRelease(version string, rels []Release) (Release, error) {
|
||||
if len(rels) == 0 {
|
||||
return Release{}, ErrVersionUnknown
|
||||
}
|
||||
|
||||
if prerelease {
|
||||
// We are a beta version. Use the latest.
|
||||
return rels[0], nil
|
||||
}
|
||||
sort.Sort(SortByRelease(rels))
|
||||
// Check for a beta build
|
||||
beta := strings.Contains(version, "-beta")
|
||||
|
||||
// We are a regular release. Only consider non-prerelease versions for upgrade.
|
||||
for _, rel := range rels {
|
||||
if !rel.Prerelease {
|
||||
return rel, nil
|
||||
if rel.Prerelease && !beta {
|
||||
continue
|
||||
}
|
||||
for _, asset := range rel.Assets {
|
||||
assetName := path.Base(asset.Name)
|
||||
// Check for the architecture
|
||||
expectedRelease := releaseName(rel.Tag)
|
||||
if debug {
|
||||
l.Debugf("expected release asset %q", expectedRelease)
|
||||
}
|
||||
if debug {
|
||||
l.Debugln("considering release", assetName)
|
||||
}
|
||||
if strings.HasPrefix(assetName, expectedRelease) {
|
||||
return rel, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return Release{}, ErrVersionUnknown
|
||||
|
||||
@@ -4,11 +4,17 @@
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
// +build !noupgrade
|
||||
|
||||
package upgrade
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var testcases = []struct {
|
||||
var versions = []struct {
|
||||
a, b string
|
||||
r Relation
|
||||
}{
|
||||
@@ -27,6 +33,7 @@ var testcases = []struct {
|
||||
{"0.10.0", "0.2.0", MajorNewer},
|
||||
{"30.10.0", "4.9.0", MajorNewer},
|
||||
{"0.9.0-beta7", "0.9.0-beta6", Newer},
|
||||
{"0.9.0-beta7", "1.0.0-alpha", MajorOlder},
|
||||
{"1.0.0-alpha", "1.0.0-alpha.1", Older},
|
||||
{"1.0.0-alpha.1", "1.0.0-alpha.beta", Older},
|
||||
{"1.0.0-alpha.beta", "1.0.0-beta", Older},
|
||||
@@ -44,9 +51,46 @@ var testcases = []struct {
|
||||
}
|
||||
|
||||
func TestCompareVersions(t *testing.T) {
|
||||
for _, tc := range testcases {
|
||||
if r := CompareVersions(tc.a, tc.b); r != tc.r {
|
||||
t.Errorf("compareVersions(%q, %q): %d != %d", tc.a, tc.b, r, tc.r)
|
||||
for _, v := range versions {
|
||||
if r := CompareVersions(v.a, v.b); r != v.r {
|
||||
t.Errorf("compareVersions(%q, %q): %d != %d", v.a, v.b, r, v.r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var upgrades = map[string]string{
|
||||
"v0.10.21": "v0.10.30",
|
||||
"v0.10.29": "v0.10.30",
|
||||
"v0.10.31": "v0.10.30",
|
||||
"v0.10.0-alpha": "v0.10.30",
|
||||
"v0.10.0-beta": "v0.11.0-beta0",
|
||||
"v0.11.0-beta0+40-g53cb66e-dirty": "v0.11.0-beta0",
|
||||
}
|
||||
|
||||
func TestGithubRelease(t *testing.T) {
|
||||
fd, err := os.Open("testdata/github-releases.json")
|
||||
if err != nil {
|
||||
t.Errorf("Missing github-release test data")
|
||||
}
|
||||
defer fd.Close()
|
||||
|
||||
var rels []Release
|
||||
json.NewDecoder(fd).Decode(&rels)
|
||||
|
||||
for old, target := range upgrades {
|
||||
upgrade, err := SelectLatestRelease(old, rels)
|
||||
if err != nil {
|
||||
t.Error("Error retrieving latest version", err)
|
||||
}
|
||||
if upgrade.Tag != target {
|
||||
t.Errorf("Invalid upgrade release: %v -> %v, but got %v", old, target, upgrade.Tag)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorRelease(t *testing.T) {
|
||||
_, err := SelectLatestRelease("v0.11.0-beta", nil)
|
||||
if err == nil {
|
||||
t.Error("Should return an error when no release were available")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,6 @@ func upgradeToURL(binary, url string) error {
|
||||
return ErrUpgradeUnsupported
|
||||
}
|
||||
|
||||
func LatestRelease(prerelease bool) (Release, error) {
|
||||
func LatestRelease(version string) (Release, error) {
|
||||
return Release{}, ErrUpgradeUnsupported
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user