Use lowerCamelCase for the JSON API (fixes #1338)

Replace the current mix of UpperCamelCase und lowerCamelCase with
consistent lowerCamelCase keys for the JSON API. Also adapt the frontend
so it works with the changed API.

Attention: this will break existing consumers of the API.
This commit is contained in:
Pascal Jungblut 2015-03-10 23:45:43 +01:00
parent 51c932164f
commit 49bc74e7a0
10 changed files with 339 additions and 329 deletions

View File

@ -46,8 +46,8 @@ import (
)
type guiError struct {
Time time.Time
Error string
Time time.Time `json:"time"`
Error string `json:"error"`
}
var (
@ -824,12 +824,12 @@ func toNeedSlice(fs []db.FileInfoTruncated) []map[string]interface{} {
output := make([]map[string]interface{}, len(fs))
for i, file := range fs {
output[i] = map[string]interface{}{
"Name": file.Name,
"Flags": file.Flags,
"Modified": file.Modified,
"Version": file.Version,
"LocalVersion": file.LocalVersion,
"Size": file.Size(),
"name": file.Name,
"flags": file.Flags,
"modified": file.Modified,
"version": file.Version,
"localVersion": file.LocalVersion,
"size": file.Size(),
}
}
return output

View File

@ -155,7 +155,7 @@
<div class="panel panel-warning">
<div class="panel-heading"><h3 class="panel-title"><span class="glyphicon glyphicon-exclamation-sign"></span><span translate>Notice</span></h3></div>
<div class="panel-body">
<p ng-repeat="err in errorList()"><small>{{err.Time | date:"H:mm:ss"}}:</small> {{friendlyDevices(err.Error)}}</p>
<p ng-repeat="err in errorList()"><small>{{err.time | date:"H:mm:ss"}}:</small> {{friendlyDevices(err.error)}}</p>
</div>
<div class="panel-footer">
<button type="button" class="pull-right btn btn-sm btn-default" ng-click="clearErrors()"><span class="glyphicon glyphicon-ok"></span>&emsp;<span translate>OK</span></button>
@ -176,9 +176,9 @@
<div class="visible-xs"><h3><span translate>Folders</span></h3><hr></div>
<div class="panel panel-default" ng-repeat="folder in folderList()">
<div class="panel-heading" data-toggle="collapse" data-parent="#folders" href="#folder-{{$index}}" style="cursor: pointer">
<div class="panel-progress" ng-show="folderStatus(folder) == 'syncing'" ng-attr-style="width: {{syncPercentage(folder.ID)}}%"></div>
<div class="panel-progress" ng-show="folderStatus(folder) == 'syncing'" ng-attr-style="width: {{syncPercentage(folder.id)}}%"></div>
<h3 class="panel-title">
<span class="glyphicon glyphicon-hdd hidden-xs"></span>{{folder.ID}}
<span class="glyphicon glyphicon-hdd hidden-xs"></span>{{folder.id}}
<span class="pull-right text-{{folderClass(folder)}}" ng-switch="folderStatus(folder)">
<span ng-switch-when="unknown"><span class="hidden-xs" translate>Unknown</span><span class="visible-xs">&#9724;</span></span>
<span ng-switch-when="unshared"><span class="hidden-xs" translate>Unshared</span><span class="visible-xs">&#9724;</span></span>
@ -187,7 +187,7 @@
<span ng-switch-when="idle"><span class="hidden-xs" translate>Up to Date</span><span class="visible-xs">&#9724;</span></span>
<span ng-switch-when="syncing">
<span class="hidden-xs" translate>Syncing</span>
({{syncPercentage(folder.ID)}}%)
({{syncPercentage(folder.id)}}%)
</span>
</span>
</h3>
@ -198,49 +198,49 @@
<tbody>
<tr>
<th><span class="glyphicon glyphicon-folder-open"></span>&emsp;<span translate>Folder Path</span></th>
<td class="text-right">{{folder.Path}}</td>
<td class="text-right">{{folder.path}}</td>
</tr>
<tr ng-if="model[folder.ID].invalid">
<tr ng-if="model[folder.id].invalid">
<th><span class="glyphicon glyphicon-warning-sign"></span>&emsp;<span translate>Error</span></th>
<td class="text-right">{{model[folder.ID].invalid}}</td>
<td class="text-right">{{model[folder.id].invalid}}</td>
</tr>
<tr>
<th><span class="glyphicon glyphicon-globe"></span>&emsp;<span translate>Global State</span></th>
<td class="text-right">{{model[folder.ID].globalFiles | alwaysNumber}} <span translate>items</span>, ~{{model[folder.ID].globalBytes | binary}}B</td>
<td class="text-right">{{model[folder.id].globalFiles | alwaysNumber}} <span translate>items</span>, ~{{model[folder.id].globalBytes | binary}}B</td>
</tr>
<tr>
<th><span class="glyphicon glyphicon-home"></span>&emsp;<span translate>Local State</span></th>
<td class="text-right">{{model[folder.ID].localFiles | alwaysNumber}} <span translate>items</span>, ~{{model[folder.ID].localBytes | binary}}B</td>
<td class="text-right">{{model[folder.id].localFiles | alwaysNumber}} <span translate>items</span>, ~{{model[folder.id].localBytes | binary}}B</td>
</tr>
<tr ng-if="model[folder.ID].needFiles > 0">
<tr ng-if="model[folder.id].needFiles > 0">
<th><span class="glyphicon glyphicon-cloud-download"></span>&emsp;<span translate>Out Of Sync</span></th>
<td class="text-right">
<a ng-click="showNeed(folder.ID)" href="">{{model[folder.ID].needFiles | alwaysNumber}} <span translate>items</span>, ~{{model[folder.ID].needBytes | binary}}B</a>
<a ng-click="showNeed(folder.id)" href="">{{model[folder.id].needFiles | alwaysNumber}} <span translate>items</span>, ~{{model[folder.id].needBytes | binary}}B</a>
</td>
</tr>
<tr ng-if="folder.ReadOnly">
<tr ng-if="folder.readOnly">
<th><span class="glyphicon glyphicon-lock"></span>&emsp;<span translate>Folder Master</span></th>
<td class="text-right">
<span translate>Yes</span>
</td>
</tr>
<tr ng-if="model[folder.ID].ignorePatterns">
<tr ng-if="model[folder.id].ignorePatterns">
<th><span class="glyphicon glyphicon-eye-close"></span>&emsp;<span translate>Ignore Patterns</span></th>
<td class="text-right">
<span translate>Yes</span>
</td>
</tr>
<tr ng-if="folder.IgnorePerms">
<tr ng-if="folder.ignorePerms">
<th><span class="glyphicon glyphicon-unchecked"></span>&emsp;<span translate>Ignore Permissions</span></th>
<td class="text-right">
<span translate>Yes</span>
</td>
</tr>
<tr ng-if="folder.RescanIntervalS != 60">
<tr ng-if="folder.rescanIntervalS != 60">
<th><span class="glyphicon glyphicon-refresh"></span>&emsp;<span translate>Rescan Interval</span></th>
<td class="text-right">{{folder.RescanIntervalS}} s</td>
<td class="text-right">{{folder.rescanIntervalS}} s</td>
</tr>
<tr ng-if="folder.Versioning.Type">
<tr ng-if="folder.versioning.type">
<th><span class="glyphicon glyphicon-tags"></span>&emsp;<span translate>File Versioning</span></th>
<td class="text-right" ng-switch="folder.Versioning.Type">
<span ng-switch-when="staggered" translate>Staggered File Versioning</span>
@ -251,11 +251,11 @@
<th><span class="glyphicon glyphicon-share-alt"></span>&emsp;<span translate>Shared With</span></th>
<td class="text-right">{{sharesFolder(folder)}}</td>
</tr>
<tr ng-if="folderStats[folder.ID].LastFile">
<tr ng-if="folderStats[folder.id].lastFile">
<th><span class="glyphicon glyphicon-transfer"></span>&emsp;<span translate>Last File Received</span></th>
<td class="text-right">
<span title="{{folderStats[folder.ID].LastFile.Filename}} @ {{folderStats[folder.ID].LastFile.At | date:'yyyy-MM-dd HH:mm'}}">
{{folderStats[folder.ID].LastFile.Filename | basename}}
<span title="{{folderStats[folder.id].lastFile.filename}} @ {{folderStats[folder.id].lastFile.at | date:'yyyy-MM-dd HH:mm'}}">
{{folderStats[folder.id].lastFile.filename | basename}}
</span>
</td>
</tr>
@ -263,9 +263,9 @@
</table>
</div>
<div class="panel-footer">
<button class="btn btn-sm btn-danger pull-left" ng-if="folder.ReadOnly && model[folder.ID].needFiles > 0" ng-click="override(folder.ID)" href=""><span class="glyphicon glyphicon-upload"></span>&emsp;<span translate>Override Changes</span></button>
<button class="btn btn-sm btn-danger pull-left" ng-if="folder.readOnly && model[folder.id].needFiles > 0" ng-click="override(folder.id)" href=""><span class="glyphicon glyphicon-upload"></span>&emsp;<span translate>Override Changes</span></button>
<span class="pull-right">
<button class="btn btn-sm btn-default" href="" ng-show="folderStatus(folder) == 'idle'" ng-click="rescanFolder(folder.ID)"><span class="glyphicon glyphicon-refresh"></span>&emsp;<span translate>Rescan</span></button>
<button class="btn btn-sm btn-default" href="" ng-show="folderStatus(folder) == 'idle'" ng-click="rescanFolder(folder.id)"><span class="glyphicon glyphicon-refresh"></span>&emsp;<span translate>Rescan</span></button>
<button class="btn btn-sm btn-default" href="" ng-click="editFolder(folder)"><span class="glyphicon glyphicon-pencil"></span>&emsp;<span translate>Edit</span></button>
</span>
<div class="clearfix"></div>
@ -290,7 +290,7 @@
<div class="panel panel-default" ng-repeat="deviceCfg in [thisDevice()]">
<div class="panel-heading" data-toggle="collapse" href="#device-this" style="cursor: pointer">
<h3 class="panel-title">
<identicon data-value="deviceCfg.DeviceID"></identicon>&emsp;{{deviceName(deviceCfg)}}
<identicon data-value="deviceCfg.deviceID"></identicon>&emsp;{{deviceName(deviceCfg)}}
</h3>
</div>
<div id="device-this" class="panel-collapse collapse in">
@ -299,11 +299,11 @@
<tbody>
<tr>
<th><span class="glyphicon glyphicon-cloud-download"></span>&emsp;<span translate>Download Rate</span></th>
<td class="text-right">{{connections['total'].inbps | binary}}B/s ({{connections['total'].InBytesTotal | binary}}B)</td>
<td class="text-right">{{connections['total'].inbps | binary}}B/s ({{connections['total'].inBytesTotal | binary}}B)</td>
</tr>
<tr>
<th><span class="glyphicon glyphicon-cloud-upload"></span>&emsp;<span translate>Upload Rate</span></th>
<td class="text-right">{{connections['total'].outbps | binary}}B/s ({{connections['total'].OutBytesTotal | binary}}B)</td>
<td class="text-right">{{connections['total'].outbps | binary}}B/s ({{connections['total'].outBytesTotal | binary}}B)</td>
</tr>
<tr>
<th><span class="glyphicon glyphicon-th"></span>&emsp;<span translate>RAM Utilization</span></th>
@ -341,13 +341,13 @@
<div class="panel-group" id="devices">
<div class="panel panel-default" ng-repeat="deviceCfg in otherDevices()">
<div class="panel-heading" data-toggle="collapse" data-parent="#devices" href="#device-{{$index}}" style="cursor: pointer">
<div class="panel-progress" ng-show="deviceStatus(deviceCfg) == 'syncing'" ng-attr-style="width: {{completion[deviceCfg.DeviceID]._total | number:0}}%"></div>
<div class="panel-progress" ng-show="deviceStatus(deviceCfg) == 'syncing'" ng-attr-style="width: {{completion[deviceCfg.deviceID]._total | number:0}}%"></div>
<h3 class="panel-title">
<identicon data-value="deviceCfg.DeviceID"></identicon>&emsp;{{deviceName(deviceCfg)}}
<identicon data-value="deviceCfg.deviceID"></identicon>&emsp;{{deviceName(deviceCfg)}}
<span ng-switch="deviceStatus(deviceCfg)" class="pull-right text-{{deviceClass(deviceCfg)}}">
<span ng-switch-when="insync"><span class="hidden-xs" translate>Up to Date</span><span class="visible-xs">&#9724;</span></span>
<span ng-switch-when="syncing">
<span class="hidden-xs" translate>Syncing</span> ({{completion[deviceCfg.DeviceID]._total | number:0}}%)
<span class="hidden-xs" translate>Syncing</span> ({{completion[deviceCfg.deviceID]._total | number:0}}%)
</span>
<span ng-switch-when="disconnected"><span class="hidden-xs" translate>Disconnected</span><span class="visible-xs">&#9724;</span></span>
<span ng-switch-when="unused"><span class="hidden-xs" translate>Unused</span><span class="visible-xs">&#9724;</span></span>
@ -358,37 +358,37 @@
<div class="panel-body">
<table class="table table-condensed table-striped">
<tbody>
<tr ng-if="connections[deviceCfg.DeviceID]">
<tr ng-if="connections[deviceCfg.deviceID]">
<th><span class="glyphicon glyphicon-cloud-download"></span>&emsp;<span translate>Download Rate</span></th>
<td class="text-right">{{connections[deviceCfg.DeviceID].inbps | binary}}B/s ({{connections[deviceCfg.DeviceID].InBytesTotal | binary}}B)</td>
<td class="text-right">{{connections[deviceCfg.deviceID].inbps | binary}}B/s ({{connections[deviceCfg.deviceID].inBytesTotal | binary}}B)</td>
</tr>
<tr ng-if="connections[deviceCfg.DeviceID]">
<tr ng-if="connections[deviceCfg.deviceID]">
<th><span class="glyphicon glyphicon-cloud-upload"></span>&emsp;<span translate>Upload Rate</span></th>
<td class="text-right">{{connections[deviceCfg.DeviceID].outbps | binary}}B/s ({{connections[deviceCfg.DeviceID].OutBytesTotal | binary}}B)</td>
<td class="text-right">{{connections[deviceCfg.deviceID].outbps | binary}}B/s ({{connections[deviceCfg.deviceID].outBytesTotal | binary}}B)</td>
</tr>
<tr>
<th><span class="glyphicon glyphicon-link"></span>&emsp;<span translate>Address</span></th>
<td class="text-right">{{deviceAddr(deviceCfg)}}</td>
</tr>
<tr ng-if="deviceCfg.Compression != 'metadata'">
<tr ng-if="deviceCfg.compression != 'metadata'">
<th><span class="glyphicon glyphicon-compressed"></span>&emsp;<span translate>Compression</span></th>
<td class="text-right">
<span ng-if="deviceCfg.Compression == 'always'" translate>All Data</span>
<span ng-if="deviceCfg.Compression == 'never'" translate>Off</span>
<span ng-if="deviceCfg.compression == 'always'" translate>All Data</span>
<span ng-if="deviceCfg.compression == 'never'" translate>Off</span>
</td>
</tr>
<tr ng-if="deviceCfg.Introducer">
<tr ng-if="deviceCfg.introducer">
<th><span class="glyphicon glyphicon-thumbs-up"></span>&emsp;<span translate>Introducer</span></th>
<td translate class="text-right">Yes</td>
</tr>
<tr ng-if="connections[deviceCfg.DeviceID]">
<tr ng-if="connections[deviceCfg.deviceID]">
<th><span class="glyphicon glyphicon-tag"></span>&emsp;<span translate>Version</span></th>
<td class="text-right">{{connections[deviceCfg.DeviceID].ClientVersion}}</td>
<td class="text-right">{{connections[deviceCfg.deviceID].clientVersion}}</td>
</tr>
<tr ng-if="!connections[deviceCfg.DeviceID]">
<tr ng-if="!connections[deviceCfg.deviceID]">
<th><span class="glyphicon glyphicon-eye-open"></span>&emsp;<span translate>Last seen</span></th>
<td translate ng-if="!deviceStats[deviceCfg.DeviceID].LastSeenDays || deviceStats[deviceCfg.DeviceID].LastSeenDays >= 365" class="text-right">Never</td>
<td ng-if="deviceStats[deviceCfg.DeviceID].LastSeenDays < 365" class="text-right">{{deviceStats[deviceCfg.DeviceID].LastSeen | date:"yyyy-MM-dd HH:mm"}}</td>
<td translate ng-if="!deviceStats[deviceCfg.deviceID].lastSeenDays || deviceStats[deviceCfg.deviceID].lastSeenDays >= 365" class="text-right">Never</td>
<td ng-if="deviceStats[deviceCfg.deviceID].lastSeenDays < 365" class="text-right">{{deviceStats[deviceCfg.deviceID].lastSeen | date:"yyyy-MM-dd HH:mm"}}</td>
</tr>
<tr ng-if="deviceFolders(deviceCfg).length > 0">
<th><span class="glyphicon glyphicon-hdd"></span>&emsp;<span translate>Folders</span></th>
@ -481,11 +481,11 @@
<form role="form" name="deviceEditor">
<div class="form-group" ng-class="{'has-error': deviceEditor.deviceID.$invalid && deviceEditor.deviceID.$dirty}">
<label translate for="deviceID">Device ID</label>
<input ng-if="!editingExisting" name="deviceID" id="deviceID" class="form-control text-monospace" type="text" ng-model="currentDevice.DeviceID" required valid-deviceid list="discovery-list" />
<input ng-if="!editingExisting" name="deviceID" id="deviceID" class="form-control text-monospace" type="text" ng-model="currentDevice.deviceID" required valid-deviceid list="discovery-list" />
<datalist id="discovery-list" ng-if="!editingExisting">
<option ng-repeat="(id,address) in discovery" value="{{ id }}" />
</datalist>
<div ng-if="editingExisting" class="well well-sm text-monospace">{{currentDevice.DeviceID}}</div>
<div ng-if="editingExisting" class="well well-sm text-monospace">{{currentDevice.deviceID}}</div>
<p class="help-block">
<span translate ng-if="deviceEditor.deviceID.$valid || deviceEditor.deviceID.$pristine">The device ID to enter here can be found in the "Edit > Show ID" dialog on the other device. Spaces and dashes are optional (ignored).</span>
<span translate ng-show="!editingExisting && (deviceEditor.deviceID.$valid || deviceEditor.deviceID.$pristine)">When adding a new device, keep in mind that this device must be added on the other side too.</span>
@ -495,18 +495,18 @@
</div>
<div class="form-group">
<label translate for="name">Device Name</label>
<input id="name" class="form-control" type="text" ng-model="currentDevice.Name"></input>
<p translate ng-if="currentDevice.DeviceID == myID" class="help-block">Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.</p>
<p translate ng-if="currentDevice.DeviceID != myID" class="help-block">Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.</p>
<input id="name" class="form-control" type="text" ng-model="currentDevice.name"></input>
<p translate ng-if="currentDevice.deviceID == myID" class="help-block">Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.</p>
<p translate ng-if="currentDevice.deviceID != myID" class="help-block">Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.</p>
</div>
<div class="form-group">
<label translate for="addresses">Addresses</label>
<input ng-disabled="currentDevice.DeviceID == myID" id="addresses" class="form-control" type="text" ng-model="currentDevice.AddressesStr"></input>
<input ng-disabled="currentDevice.deviceID == myID" id="addresses" class="form-control" type="text" ng-model="currentDevice.addressesStr"></input>
<p translate class="help-block">Enter comma separated "ip:port" addresses or "dynamic" to perform automatic discovery of the address.</p>
</div>
<div ng-if="!editingSelf" class="form-group">
<label translate>Compression</label>
<select class="form-control" ng-model="currentDevice.Compression">
<select class="form-control" ng-model="currentDevice.compression">
<option value="always" translate>All Data</option>
<option value="metadata" translate>Metadata Only</option>
<option value="never" translate>Off</option>
@ -515,7 +515,7 @@
<div ng-if="!editingSelf" class="form-group">
<div class="checkbox">
<label>
<input type="checkbox" ng-model="currentDevice.Introducer"> <span translate>Introducer</span>
<input type="checkbox" ng-model="currentDevice.introducer"> <span translate>Introducer</span>
</label>
<p translate class="help-block">Any devices configured on an introducer device will be added to this device as well.</p>
</div>
@ -529,7 +529,7 @@
<div class="three-columns">
<div class="checkbox" ng-repeat="folder in folderList()">
<label>
<input type="checkbox" ng-model="currentDevice.selectedFolders[folder.ID]"> {{folder.ID}}
<input type="checkbox" ng-model="currentDevice.selectedFolders[folder.id]"> {{folder.id}}
</label>
</div>
</div>
@ -563,7 +563,7 @@
<div class="col-md-12">
<div class="form-group" ng-class="{'has-error': folderEditor.folderID.$invalid && folderEditor.folderID.$dirty}">
<label for="folderID"><span translate>Folder ID</span></label>
<input name="folderID" ng-readonly="editingExisting" id="folderID" class="form-control" type="text" ng-model="currentFolder.ID" required unique-folder ng-pattern="/^[a-zA-Z0-9-_.]{1,64}$/"></input>
<input name="folderID" ng-readonly="editingExisting" id="folderID" class="form-control" type="text" ng-model="currentFolder.id" required unique-folder ng-pattern="/^[a-zA-Z0-9-_.]{1,64}$/"></input>
<p class="help-block">
<span translate ng-if="folderEditor.folderID.$valid || folderEditor.folderID.$pristine">Short identifier for the folder. Must be the same on all cluster devices.</span>
<span translate ng-if="folderEditor.folderID.$error.uniqueFolder">The folder ID must be unique.</span>
@ -573,7 +573,7 @@
</div>
<div class="form-group" ng-class="{'has-error': folderEditor.folderPath.$invalid && folderEditor.folderPath.$dirty}">
<label translate for="folderPath">Folder Path</label>
<input name="folderPath" ng-readonly="editingExisting" id="folderPath" class="form-control" type="text" ng-model="currentFolder.Path" list="directory-list" required />
<input name="folderPath" ng-readonly="editingExisting" id="folderPath" class="form-control" type="text" ng-model="currentFolder.path" list="directory-list" required />
<datalist id="directory-list">
<option ng-repeat="directory in directoryList" value="{{ directory }}" />
</datalist>
@ -584,7 +584,7 @@
</div>
<div class="form-group" ng-class="{'has-error': folderEditor.rescanIntervalS.$invalid && folderEditor.rescanIntervalS.$dirty}">
<label for="rescanIntervalS"><span translate>Rescan Interval</span> (s)</label>
<input name="rescanIntervalS" id="rescanIntervalS" class="form-control" type="number" ng-model="currentFolder.RescanIntervalS" required min="0"></input>
<input name="rescanIntervalS" id="rescanIntervalS" class="form-control" type="number" ng-model="currentFolder.rescanIntervalS" required min="0"></input>
<p class="help-block">
<span translate ng-if="!folderEditor.rescanIntervalS.$valid && folderEditor.rescanIntervalS.$dirty">The rescan interval must be a non-negative number of seconds.</span>
</p>
@ -596,7 +596,7 @@
<div class="form-group">
<div class="checkbox">
<label>
<input type="checkbox" ng-model="currentFolder.ReadOnly"> <span translate>Folder Master</span>
<input type="checkbox" ng-model="currentFolder.readOnly"> <span translate>Folder Master</span>
</label>
</div>
<p translate class="help-block">Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.</p>
@ -604,7 +604,7 @@
<div class="form-group">
<div class="checkbox">
<label>
<input type="checkbox" ng-model="currentFolder.IgnorePerms"> <span translate>Ignore Permissions</span>
<input type="checkbox" ng-model="currentFolder.ignorePerms"> <span translate>Ignore Permissions</span>
</label>
</div>
<p translate class="help-block">File permission bits are ignored when looking for changes. Use on FAT filesystems.</p>
@ -615,21 +615,21 @@
<label translate>File Versioning</label>
<div class="radio">
<label>
<input type="radio" ng-model="currentFolder.FileVersioningSelector" value="none"> <span translate>No File Versioning</span>
<input type="radio" ng-model="currentFolder.fileVersioningSelector" value="none"> <span translate>No File Versioning</span>
</label>
</div>
<div class="radio">
<label>
<input type="radio" ng-model="currentFolder.FileVersioningSelector" value="simple"> <span translate>Simple File Versioning</span>
<input type="radio" ng-model="currentFolder.fileVersioningSelector" value="simple"> <span translate>Simple File Versioning</span>
</label>
</div>
<div class="radio">
<label>
<input type="radio" ng-model="currentFolder.FileVersioningSelector" value="staggered"> <span translate>Staggered File Versioning</span>
<input type="radio" ng-model="currentFolder.fileVersioningSelector" value="staggered"> <span translate>Staggered File Versioning</span>
</label>
</div>
</div>
<div class="form-group" ng-if="currentFolder.FileVersioningSelector=='simple'" ng-class="{'has-error': folderEditor.simpleKeep.$invalid && folderEditor.simpleKeep.$dirty}">
<div class="form-group" ng-if="currentFolder.fileVersioningSelector=='simple'" ng-class="{'has-error': folderEditor.simpleKeep.$invalid && folderEditor.simpleKeep.$dirty}">
<p translate class="help-block">Files are moved to date stamped versions in a .stversions folder when replaced or deleted by syncthing.</p>
<label translate for="simpleKeep">Keep Versions</label>
<input name="simpleKeep" id="simpleKeep" class="form-control" type="number" ng-model="currentFolder.simpleKeep" required min="1"></input>
@ -639,7 +639,7 @@
<span translate ng-if="folderEditor.simpleKeep.$error.min && folderEditor.simpleKeep.$dirty">You must keep at least one version.</span>
</p>
</div>
<div class="form-group" ng-if="currentFolder.FileVersioningSelector=='staggered'" ng-class="{'has-error': folderEditor.staggeredMaxAge.$invalid && folderEditor.staggeredMaxAge.$dirty}">
<div class="form-group" ng-if="currentFolder.fileVersioningSelector=='staggered'" ng-class="{'has-error': folderEditor.staggeredMaxAge.$invalid && folderEditor.staggeredMaxAge.$dirty}">
<p class="help-block"><span translate>Files are moved to date stamped versions in a .stversions folder when replaced or deleted by syncthing.</span> <span translate>Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.</span></p>
<p translate class="help-block">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.</p>
<label translate for="staggeredMaxAge">Maximum Age</label>
@ -649,7 +649,7 @@
<span translate ng-if="folderEditor.staggeredMaxAge.$error.required && folderEditor.staggeredMaxAge.$dirty">The maximum age must be a number and cannot be blank.</span>
</p>
</div>
<div class="form-group" ng-if="currentFolder.FileVersioningSelector == 'staggered'">
<div class="form-group" ng-if="currentFolder.fileVersioningSelector == 'staggered'">
<label translate for="staggeredVersionsPath">Versions Path</label>
<input name="staggeredVersionsPath" id="staggeredVersionsPath" class="form-control" type="text" ng-model="currentFolder.staggeredVersionsPath"></input>
<p translate class="help-block">Path where versions should be stored (leave empty for the default .stversions folder in the folder).</p>
@ -665,7 +665,7 @@
<div class="three-columns">
<div class="checkbox" ng-repeat="device in otherDevices()">
<label>
<input type="checkbox" ng-model="currentFolder.selectedDevices[device.DeviceID]"> {{deviceName(device)}}
<input type="checkbox" ng-model="currentFolder.selectedDevices[device.deviceID]"> {{deviceName(device)}}
</label>
</div>
</div>
@ -709,7 +709,7 @@
</dl>
</div>
<div class="modal-footer">
<div class="pull-left"><span translate>Editing</span> <code>{{currentFolder.Path}}{{system.pathSeparator}}.stignore</code></div>
<div class="pull-left"><span translate>Editing</span> <code>{{currentFolder.path}}{{system.pathSeparator}}.stignore</code></div>
<button type="button" class="btn btn-primary btn-sm" data-dismiss="modal" ng-click="saveIgnores()"><span class="glyphicon glyphicon-ok"></span>&emsp;<span translate>Save</span></button>
<button type="button" class="btn btn-default btn-sm" data-dismiss="modal"><span class="glyphicon glyphicon-remove"></span>&emsp;<span translate>Close</span></button>
</div>
@ -732,32 +732,32 @@
<div class="col-md-6">
<div class="form-group">
<label translate for="DeviceName">Device Name</label>
<input id="DeviceName" class="form-control" type="text" ng-model="tmpOptions.DeviceName">
<input id="DeviceName" class="form-control" type="text" ng-model="tmpOptions.deviceName">
</div>
<div class="form-group">
<label translate for="ListenAddressStr">Sync Protocol Listen Addresses</label>
<input id="ListenAddressStr" class="form-control" type="text" ng-model="tmpOptions.ListenAddressStr">
<input id="ListenAddressStr" class="form-control" type="text" ng-model="tmpOptions.listenAddressStr">
</div>
<div class="form-group">
<label translate for="MaxRecvKbps">Incoming Rate Limit (KiB/s)</label>
<input id="MaxRecvKbps" class="form-control" type="number" ng-model="tmpOptions.MaxRecvKbps">
<input id="MaxRecvKbps" class="form-control" type="number" ng-model="tmpOptions.maxRecvKbps">
</div>
<div class="form-group">
<label translate for="MaxSendKbps">Outgoing Rate Limit (KiB/s)</label>
<input id="MaxSendKbps" class="form-control" type="number" ng-model="tmpOptions.MaxSendKbps">
<input id="MaxSendKbps" class="form-control" type="number" ng-model="tmpOptions.maxSendKbps">
</div>
<div class="col-md-6">
<div class="form-group">
<div class="checkbox">
<label>
<input id="UPnPEnabled" type="checkbox" ng-model="tmpOptions.UPnPEnabled"> <span translate>Enable UPnP</span>
<input id="UPnPEnabled" type="checkbox" ng-model="tmpOptions.upnpEnabled"> <span translate>Enable UPnP</span>
</label>
</div>
</div>
<div class="form-group">
<div class="checkbox">
<label>
<input id="GlobalAnnEnabled" type="checkbox" ng-model="tmpOptions.GlobalAnnEnabled"> <span translate>Global Discovery</span>
<input id="GlobalAnnEnabled" type="checkbox" ng-model="tmpOptions.globalAnnounceEnabled"> <span translate>Global Discovery</span>
</label>
</div>
</div>
@ -766,55 +766,55 @@
<div class="form-group">
<div class="checkbox">
<label ng-if="upgradeInfo">
<input id="AutoUpgradeEnabled" type="checkbox" ng-model="tmpOptions.AutoUpgradeEnabled"> <span translate>Automatic upgrades</span>
<input id="AutoUpgradeEnabled" type="checkbox" ng-model="tmpOptions.autoUpgradeEnabled"> <span translate>Automatic upgrades</span>
</label>
</div>
</div>
<div class="form-group">
<div class="checkbox">
<label>
<input id="LocalAnnEnabled" type="checkbox" ng-model="tmpOptions.LocalAnnEnabled"> <span translate>Local Discovery</span>
<input id="LocalAnnEnabled" type="checkbox" ng-model="tmpOptions.localAnnounceEnabled"> <span translate>Local Discovery</span>
</label>
</div>
</div>
</div>
<div class="form-group">
<label translate for="GlobalAnnServersStr">Global Discovery Server</label>
<input ng-disabled="!tmpOptions.GlobalAnnEnabled" id="GlobalAnnServersStr" class="form-control" type="text" ng-model="tmpOptions.GlobalAnnServersStr">
<input ng-disabled="!tmpOptions.globalAnnounceEnabled" id="GlobalAnnServersStr" class="form-control" type="text" ng-model="tmpOptions.globalAnnounceServersStr">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label translate for="Address">GUI Listen Addresses</label>
<input id="Address" class="form-control" type="text" ng-model="tmpGUI.Address">
<input id="Address" class="form-control" type="text" ng-model="tmpGUI.address">
</div>
<div class="form-group">
<label translate for="User">GUI Authentication User</label>
<input id="User" class="form-control" type="text" ng-model="tmpGUI.User">
<input id="User" class="form-control" type="text" ng-model="tmpGUI.user">
</div>
<div class="form-group">
<label translate for="Password">GUI Authentication Password</label>
<input id="Password" class="form-control" type="password" ng-model="tmpGUI.Password">
<input id="Password" class="form-control" type="password" ng-model="tmpGUI.password">
</div>
<div class="form-group">
<div class="checkbox">
<label>
<input id="UseTLS" type="checkbox" ng-model="tmpGUI.UseTLS"> <span translate>Use HTTPS for GUI</span>
<input id="UseTLS" type="checkbox" ng-model="tmpGUI.useTLS"> <span translate>Use HTTPS for GUI</span>
</label>
</div>
</div>
<div class="form-group">
<div class="checkbox">
<label>
<input id="StartBrowser" type="checkbox" ng-model="tmpOptions.StartBrowser"> <span translate>Start Browser</span>
<input id="StartBrowser" type="checkbox" ng-model="tmpOptions.startBrowser"> <span translate>Start Browser</span>
</label>
</div>
</div>
<div class="form-group">
<div class="checkbox">
<label>
<input id="UREnabled" type="checkbox" ng-model="tmpOptions.UREnabled"> <span translate>Anonymous Usage Reporting</span> (<a translate ng-click="showURPreview()" href="#">Preview</a>)
<input id="UREnabled" type="checkbox" ng-model="tmpOptions.urEnabled"> <span translate>Anonymous Usage Reporting</span> (<a translate ng-click="showURPreview()" href="#">Preview</a>)
</label>
</div>
</div>
@ -823,7 +823,7 @@
<div class="form-group">
<label><span translate>API Key</span></label>
<div class="well well-sm text-monospace">{{tmpGUI.APIKey || "-"}}</div>
<div class="well well-sm text-monospace">{{tmpGUI.apiKey || "-"}}</div>
<button translate type="button" class="btn btn-sm btn-default" ng-click="setAPIKey(tmpGUI)">Generate</button>
</div>
</div>
@ -897,34 +897,34 @@
<table class="table table-striped table-condensed">
<tr ng-repeat="f in needed.progress" ng-init="a = needAction(f)">
<td class="small-data"><span class="glyphicon glyphicon-{{needIcons[a]}}"></span> {{needActions[a]}}</td>
<td title="{{f.Name}}">{{f.Name | basename}}</td>
<td ng-if="a == 'sync' && progress[neededFolder] && progress[neededFolder][f.Name]">
<td title="{{f.name}}">{{f.name | basename}}</td>
<td ng-if="a == 'sync' && progress[neededFolder] && progress[neededFolder][f.name]">
<div class="progress">
<div class="progress-bar progress-bar-success" style="width: {{progress[neededFolder][f.Name].Reused}}%"></div>
<div class="progress-bar" style="width: {{progress[neededFolder][f.Name].CopiedFromOrigin}}%"></div>
<div class="progress-bar progress-bar-info" style="width: {{progress[neededFolder][f.Name].CopiedFromElsewhere}}%"></div>
<div class="progress-bar progress-bar-warning" style="width: {{progress[neededFolder][f.Name].Pulled}}%"></div>
<div class="progress-bar progress-bar-danger progress-bar-striped active" style="width: {{progress[neededFolder][f.Name].Pulling}}%"></div>
<div class="progress-bar progress-bar-success" style="width: {{progress[neededFolder][f.name].reused}}%"></div>
<div class="progress-bar" style="width: {{progress[neededFolder][f.name].copiedFromOrigin}}%"></div>
<div class="progress-bar progress-bar-info" style="width: {{progress[neededFolder][f.name].copiedFromElsewhere}}%"></div>
<div class="progress-bar progress-bar-warning" style="width: {{progress[neededFolder][f.name].pulled}}%"></div>
<div class="progress-bar progress-bar-danger progress-bar-striped active" style="width: {{progress[neededFolder][f.name].pulling}}%"></div>
<span class="show frontal">
{{progress[neededFolder][f.Name].BytesDone | binary}}B / {{progress[neededFolder][f.Name].BytesTotal | binary}}B
{{progress[neededFolder][f.name].bytesDone | binary}}B / {{progress[neededFolder][f.name].bytesTotal | binary}}B
</span>
</div>
</td>
<td class="text-right small-data" ng-if="a != 'sync' || !progress[neededFolder] || !progress[neededFolder][f.Name]">
<span ng-if="f.Size > 0">{{f.Size | binary}}B</span>
<td class="text-right small-data" ng-if="a != 'sync' || !progress[neededFolder] || !progress[neededFolder][f.name]">
<span ng-if="f.size > 0">{{f.size | binary}}B</span>
</td>
</tr>
<tr ng-repeat="f in needed.queued" ng-init="a = needAction(f)">
<td class="small-data"><span class="glyphicon glyphicon-{{needIcons[a]}}"></span> {{needActions[a]}}</td>
<td><a href="" ng-if="$index != 0" ng-click="bumpFile(neededFolder, f.Name)" title="{{'Move to top of queue' | translate}}"><span class="glyphicon glyphicon-eject"></span></a><span ng-if="$index != 0">&ensp;</span><span title="{{f.Name}}">{{f.Name | basename}}</span></td>
<td><a href="" ng-if="$index != 0" ng-click="bumpFile(neededFolder, f.name)" title="{{'Move to top of queue' | translate}}"><span class="glyphicon glyphicon-eject"></span></a><span ng-if="$index != 0">&ensp;</span><span title="{{f.name}}">{{f.name | basename}}</span></td>
<td class="text-right small-data">
<span ng-if="f.Size > 0">{{f.Size | binary}}B</span>
<span ng-if="f.size > 0">{{f.size | binary}}B</span>
</td>
</tr>
<tr ng-repeat="f in needed.rest" ng-init="a = needAction(f)">
<td class="small-data"><span class="glyphicon glyphicon-{{needIcons[a]}}"></span> {{needActions[a]}}</td>
<td title="{{f.Name}}">{{f.Name | basename}}</td>
<td class="text-right small-data"><span ng-if="f.Size > 0">{{f.Size | binary}}B</span></td>
<td title="{{f.name}}">{{f.name | basename}}</td>
<td class="text-right small-data"><span ng-if="f.size > 0">{{f.size | binary}}B</span></td>
</tr>
</table>

View File

@ -78,7 +78,7 @@ function folderCompare(a, b) {
function folderMap(l) {
var m = {};
l.forEach(function (r) {
m[r.ID] = r;
m[r.id] = r;
});
return m;
}

View File

@ -141,8 +141,8 @@ angular.module('syncthing.core')
refreshFolderStats();
// Update completion status for all devices that we share this folder with.
$scope.folders[data.folder].Devices.forEach(function (deviceCfg) {
refreshCompletion(deviceCfg.DeviceID, data.folder);
$scope.folders[data.folder].devices.forEach(function (deviceCfg) {
refreshCompletion(deviceCfg.deviceID, data.folder);
});
});
@ -162,9 +162,9 @@ angular.module('syncthing.core')
$scope.connections[arg.data.id] = {
inbps: 0,
outbps: 0,
InBytesTotal: 0,
OutBytesTotal: 0,
Address: arg.data.addr
inBytesTotal: 0,
outBytesTotal: 0,
address: arg.data.addr
};
$scope.completion[arg.data.id] = {
_total: 100
@ -173,7 +173,7 @@ angular.module('syncthing.core')
});
$scope.$on('ConfigLoaded', function (event) {
if ($scope.config.Options.URAccepted === 0) {
if ($scope.config.options.urAccepted === 0) {
// If usage reporting has been neither accepted nor declined,
// we want to ask the user to make a choice. But we don't want
// to bug them during initial setup, so we set a cookie with
@ -216,23 +216,23 @@ angular.module('syncthing.core')
progress[folder] = {};
for (var file in stats[folder]) {
var s = stats[folder][file];
var reused = 100 * s.Reused / s.Total;
var copiedFromOrigin = 100 * s.CopiedFromOrigin / s.Total;
var copiedFromElsewhere = 100 * s.CopiedFromElsewhere / s.Total;
var pulled = 100 * s.Pulled / s.Total;
var pulling = 100 * s.Pulling / s.Total;
var reused = 100 * s.reused / s.total;
var copiedFromOrigin = 100 * s.copiedFromOrigin / s.total;
var copiedFromElsewhere = 100 * s.copiedFromElsewhere / s.total;
var pulled = 100 * s.pulled / s.total;
var pulling = 100 * s.pulling / s.total;
// We try to round up pulling to atleast a percent so that it would be atleast a bit visible.
if (pulling < 1 && pulled + copiedFromElsewhere + copiedFromOrigin + reused <= 99) {
pulling = 1;
}
progress[folder][file] = {
Reused: reused,
CopiedFromOrigin: copiedFromOrigin,
CopiedFromElsewhere: copiedFromElsewhere,
Pulled: pulled,
Pulling: pulling,
BytesTotal: s.BytesTotal,
BytesDone: s.BytesDone,
reused: reused,
copiedFromOrigin: copiedFromOrigin,
copiedFromElsewhere: copiedFromElsewhere,
pulled: pulled,
pulling: pulling,
bytesTotal: s.bytesTotal,
bytesDone: s.bytesDone,
};
}
}
@ -278,22 +278,21 @@ angular.module('syncthing.core')
var hasConfig = !isEmptyObject($scope.config);
$scope.config = config;
$scope.config.Options.ListenAddressStr = $scope.config.Options.ListenAddress.join(', ');
$scope.config.Options.GlobalAnnServersStr = $scope.config.Options.GlobalAnnServers.join(', ');
$scope.config.options.listenAddressStr = $scope.config.options.listenAddress.join(', ');
$scope.config.options.globalAnnounceServersStr = $scope.config.options.globalAnnounceServers.join(', ');
$scope.devices = $scope.config.Devices;
$scope.devices = $scope.config.devices;
$scope.devices.forEach(function (deviceCfg) {
$scope.completion[deviceCfg.DeviceID] = {
$scope.completion[deviceCfg.deviceID] = {
_total: 100
};
});
$scope.devices.sort(deviceCompare);
$scope.folders = folderMap($scope.config.Folders);
$scope.folders = folderMap($scope.config.folders);
Object.keys($scope.folders).forEach(function (folder) {
refreshFolder(folder);
$scope.folders[folder].Devices.forEach(function (deviceCfg) {
refreshCompletion(deviceCfg.DeviceID, folder);
$scope.folders[folder].devices.forEach(function (deviceCfg) {
refreshCompletion(deviceCfg.deviceID, folder);
});
});
@ -362,8 +361,8 @@ angular.module('syncthing.core')
continue;
}
try {
data[id].inbps = Math.max(0, (data[id].InBytesTotal - $scope.connections[id].InBytesTotal) / td);
data[id].outbps = Math.max(0, (data[id].OutBytesTotal - $scope.connections[id].OutBytesTotal) / td);
data[id].inbps = Math.max(0, (data[id].inBytesTotal - $scope.connections[id].inBytesTotal) / td);
data[id].outbps = Math.max(0, (data[id].outBytesTotal - $scope.connections[id].outBytesTotal) / td);
} catch (e) {
data[id].inbps = 0;
data[id].outbps = 0;
@ -405,8 +404,8 @@ angular.module('syncthing.core')
$http.get(urlbase + "/stats/device").success(function (data) {
$scope.deviceStats = data;
for (var device in $scope.deviceStats) {
$scope.deviceStats[device].LastSeen = new Date($scope.deviceStats[device].LastSeen);
$scope.deviceStats[device].LastSeenDays = (new Date() - $scope.deviceStats[device].LastSeen) / 1000 / 86400;
$scope.deviceStats[device].lastSeen = new Date($scope.deviceStats[device].lastSeen);
$scope.deviceStats[device].lastSeenDays = (new Date() - $scope.deviceStats[device].lastSeen) / 1000 / 86400;
}
console.log("refreshDeviceStats", data);
}).error($scope.emitHTTPError);
@ -416,8 +415,8 @@ angular.module('syncthing.core')
$http.get(urlbase + "/stats/folder").success(function (data) {
$scope.folderStats = data;
for (var folder in $scope.folderStats) {
if ($scope.folderStats[folder].LastFile) {
$scope.folderStats[folder].LastFile.At = new Date($scope.folderStats[folder].LastFile.At);
if ($scope.folderStats[folder].lastFile) {
$scope.folderStats[folder].lastFile.at = new Date($scope.folderStats[folder].lastFile.at);
}
}
console.log("refreshfolderStats", data);
@ -431,38 +430,38 @@ angular.module('syncthing.core')
};
$scope.folderStatus = function (folderCfg) {
if (typeof $scope.model[folderCfg.ID] === 'undefined') {
if (typeof $scope.model[folderCfg.id] === 'undefined') {
return 'unknown';
}
if (folderCfg.Devices.length <= 1) {
if (folderCfg.devices.length <= 1) {
return 'unshared';
}
if ($scope.model[folderCfg.ID].invalid !== '') {
if ($scope.model[folderCfg.id].invalid !== '') {
return 'stopped';
}
return '' + $scope.model[folderCfg.ID].state;
return '' + $scope.model[folderCfg.id].state;
};
$scope.folderClass = function (folderCfg) {
if (typeof $scope.model[folderCfg.ID] === 'undefined') {
if (typeof $scope.model[folderCfg.id] === 'undefined') {
// Unknown
return 'info';
}
if (folderCfg.Devices.length <= 1) {
if (folderCfg.devices.length <= 1) {
// Unshared
return 'warning';
}
if ($scope.model[folderCfg.ID].invalid !== '') {
if ($scope.model[folderCfg.id].invalid !== '') {
// Errored
return 'danger';
}
var state = '' + $scope.model[folderCfg.ID].state;
var state = '' + $scope.model[folderCfg.id].state;
if (state == 'idle') {
return 'success';
}
@ -488,8 +487,8 @@ angular.module('syncthing.core')
};
$scope.deviceIcon = function (deviceCfg) {
if ($scope.connections[deviceCfg.DeviceID]) {
if ($scope.completion[deviceCfg.DeviceID] && $scope.completion[deviceCfg.DeviceID]._total === 100) {
if ($scope.connections[deviceCfg.deviceID]) {
if ($scope.completion[deviceCfg.deviceID] && $scope.completion[deviceCfg.deviceID]._total === 100) {
return 'ok';
} else {
return 'refresh';
@ -504,8 +503,8 @@ angular.module('syncthing.core')
return 'unused';
}
if ($scope.connections[deviceCfg.DeviceID]) {
if ($scope.completion[deviceCfg.DeviceID] && $scope.completion[deviceCfg.DeviceID]._total === 100) {
if ($scope.connections[deviceCfg.deviceID]) {
if ($scope.completion[deviceCfg.deviceID] && $scope.completion[deviceCfg.deviceID]._total === 100) {
return 'insync';
} else {
return 'syncing';
@ -522,8 +521,8 @@ angular.module('syncthing.core')
return 'warning';
}
if ($scope.connections[deviceCfg.DeviceID]) {
if ($scope.completion[deviceCfg.DeviceID] && $scope.completion[deviceCfg.DeviceID]._total === 100) {
if ($scope.connections[deviceCfg.deviceID]) {
if ($scope.completion[deviceCfg.deviceID] && $scope.completion[deviceCfg.deviceID]._total === 100) {
return 'success';
} else {
return 'primary';
@ -535,24 +534,24 @@ angular.module('syncthing.core')
};
$scope.deviceAddr = function (deviceCfg) {
var conn = $scope.connections[deviceCfg.DeviceID];
var conn = $scope.connections[deviceCfg.deviceID];
if (conn) {
return conn.Address;
return conn.address;
}
return '?';
};
$scope.deviceCompletion = function (deviceCfg) {
var conn = $scope.connections[deviceCfg.DeviceID];
var conn = $scope.connections[deviceCfg.deviceID];
if (conn) {
return conn.Completion + '%';
return conn.completion + '%';
}
return '';
};
$scope.findDevice = function (deviceID) {
var matches = $scope.devices.filter(function (n) {
return n.DeviceID == deviceID;
return n.deviceID == deviceID;
});
if (matches.length != 1) {
return undefined;
@ -564,10 +563,10 @@ angular.module('syncthing.core')
if (typeof deviceCfg === 'undefined') {
return "";
}
if (deviceCfg.Name) {
return deviceCfg.Name;
if (deviceCfg.name) {
return deviceCfg.name;
}
return deviceCfg.DeviceID.substr(0, 6);
return deviceCfg.deviceID.substr(0, 6);
};
$scope.thisDeviceName = function () {
@ -575,19 +574,19 @@ angular.module('syncthing.core')
if (typeof device === 'undefined') {
return "(unknown device)";
}
if (device.Name) {
return device.Name;
if (device.name) {
return device.name;
}
return device.DeviceID.substr(0, 6);
return device.deviceID.substr(0, 6);
};
$scope.editSettings = function () {
// Make a working copy
$scope.tmpOptions = angular.copy($scope.config.Options);
$scope.tmpOptions.UREnabled = ($scope.tmpOptions.URAccepted > 0);
$scope.tmpOptions.DeviceName = $scope.thisDevice().Name;
$scope.tmpOptions.AutoUpgradeEnabled = ($scope.tmpOptions.AutoUpgradeIntervalH > 0);
$scope.tmpGUI = angular.copy($scope.config.GUI);
$scope.tmpOptions = angular.copy($scope.config.options);
$scope.tmpOptions.urEnabled = ($scope.tmpOptions.urAccepted > 0);
$scope.tmpOptions.deviceName = $scope.thisDevice().name;
$scope.tmpOptions.autoUpgradeEnabled = ($scope.tmpOptions.autoUpgradeIntervalH > 0);
$scope.tmpGUI = angular.copy($scope.config.gui);
$('#settings').modal();
};
@ -607,34 +606,34 @@ angular.module('syncthing.core')
$scope.saveSettings = function () {
// Make sure something changed
var changed = !angular.equals($scope.config.Options, $scope.tmpOptions) || !angular.equals($scope.config.GUI, $scope.tmpGUI);
var changed = !angular.equals($scope.config.options, $scope.tmpOptions) || !angular.equals($scope.config.gui, $scope.tmpGUI);
if (changed) {
// Check if usage reporting has been enabled or disabled
if ($scope.tmpOptions.UREnabled && $scope.tmpOptions.URAccepted <= 0) {
$scope.tmpOptions.URAccepted = 1000;
} else if (!$scope.tmpOptions.UREnabled && $scope.tmpOptions.URAccepted > 0) {
$scope.tmpOptions.URAccepted = -1;
if ($scope.tmpOptions.urEnabled && $scope.tmpOptions.urAccepted <= 0) {
$scope.tmpOptions.urAccepted = 1000;
} else if (!$scope.tmpOptions.urEnabled && $scope.tmpOptions.urAccepted > 0) {
$scope.tmpOptions.urAccepted = -1;
}
// Check if auto-upgrade has been enabled or disabled
if ($scope.tmpOptions.AutoUpgradeEnabled) {
$scope.tmpOptions.AutoUpgradeIntervalH = $scope.tmpOptions.AutoUpgradeIntervalH || 12;
if ($scope.tmpOptions.autoUpgradeEnabled) {
$scope.tmpOptions.autoUpgradeIntervalH = $scope.tmpOptions.autoUpgradeIntervalH || 12;
} else {
$scope.tmpOptions.AutoUpgradeIntervalH = 0;
$scope.tmpOptions.autoUpgradeIntervalH = 0;
}
// Check if protocol will need to be changed on restart
if ($scope.config.GUI.UseTLS !== $scope.tmpGUI.UseTLS) {
if ($scope.config.gui.useTLS !== $scope.tmpGUI.useTLS) {
$scope.protocolChanged = true;
}
// Apply new settings locally
$scope.thisDevice().Name = $scope.tmpOptions.DeviceName;
$scope.config.Options = angular.copy($scope.tmpOptions);
$scope.config.GUI = angular.copy($scope.tmpGUI);
$scope.thisDevice().name = $scope.tmpOptions.deviceName;
$scope.config.options = angular.copy($scope.tmpOptions);
$scope.config.gui = angular.copy($scope.tmpGUI);
['ListenAddress', 'GlobalAnnServers'].forEach(function (key) {
$scope.config.Options[key] = $scope.config.Options[key + "Str"].split(/[ ,]+/).map(function (x) {
['listenAddress', 'globalAnnounceServers'].forEach(function (key) {
$scope.config.options[key] = $scope.config.options[key + "Str"].split(/[ ,]+/).map(function (x) {
return x.trim();
});
});
@ -655,7 +654,7 @@ angular.module('syncthing.core')
if ($scope.protocolChanged) {
var protocol = 'http';
if ($scope.config.GUI.UseTLS) {
if ($scope.config.gui.useTLS) {
protocol = 'https';
}
@ -689,8 +688,8 @@ angular.module('syncthing.core')
$scope.editDevice = function (deviceCfg) {
$scope.currentDevice = $.extend({}, deviceCfg);
$scope.editingExisting = true;
$scope.editingSelf = (deviceCfg.DeviceID == $scope.myID);
$scope.currentDevice.AddressesStr = deviceCfg.Addresses.join(', ');
$scope.editingSelf = (deviceCfg.deviceID == $scope.myID);
$scope.currentDevice.addressesStr = deviceCfg.addresses.join(', ');
if (!$scope.editingSelf) {
$scope.currentDevice.selectedFolders = {};
$scope.deviceFolders($scope.currentDevice).forEach(function (folder) {
@ -712,9 +711,9 @@ angular.module('syncthing.core')
})
.then(function () {
$scope.currentDevice = {
AddressesStr: 'dynamic',
Compression: 'metadata',
Introducer: false,
addressesStr: 'dynamic',
compression: 'metadata',
introducer: false,
selectedFolders: {}
};
$scope.editingExisting = false;
@ -731,18 +730,18 @@ angular.module('syncthing.core')
}
$scope.devices = $scope.devices.filter(function (n) {
return n.DeviceID !== $scope.currentDevice.DeviceID;
return n.deviceID !== $scope.currentDevice.deviceID;
});
$scope.config.Devices = $scope.devices;
$scope.config.devices = $scope.devices;
// In case we later added the device manually, remove the ignoral
// record.
$scope.config.IgnoredDevices = $scope.config.IgnoredDevices.filter(function (id) {
return id !== $scope.currentDevice.DeviceID;
$scope.config.ignoredDevices = $scope.config.ignoredDevices.filter(function (id) {
return id !== $scope.currentDevice.deviceID;
});
for (var id in $scope.folders) {
$scope.folders[id].Devices = $scope.folders[id].Devices.filter(function (n) {
return n.DeviceID !== $scope.currentDevice.DeviceID;
$scope.folders[id].devices = $scope.folders[id].devices.filter(function (n) {
return n.deviceID !== $scope.currentDevice.deviceID;
});
}
@ -756,10 +755,10 @@ angular.module('syncthing.core')
$scope.addNewDeviceID = function (device) {
var deviceCfg = {
DeviceID: device,
AddressesStr: 'dynamic',
Compression: 'metadata',
Introducer: false,
deviceID: device,
addressesStr: 'dynamic',
compression: 'metadata',
introducer: false,
selectedFolders: {}
};
$scope.saveDeviceConfig(deviceCfg);
@ -768,13 +767,13 @@ angular.module('syncthing.core')
$scope.saveDeviceConfig = function (deviceCfg) {
var done, i;
deviceCfg.Addresses = deviceCfg.AddressesStr.split(',').map(function (x) {
deviceCfg.addresses = deviceCfg.addressesStr.split(',').map(function (x) {
return x.trim();
});
done = false;
for (i = 0; i < $scope.devices.length; i++) {
if ($scope.devices[i].DeviceID === deviceCfg.DeviceID) {
if ($scope.devices[i].deviceID === deviceCfg.deviceID) {
$scope.devices[i] = deviceCfg;
done = true;
break;
@ -786,32 +785,32 @@ angular.module('syncthing.core')
}
$scope.devices.sort(deviceCompare);
$scope.config.Devices = $scope.devices;
$scope.config.devices = $scope.devices;
// In case we are adding the device manually, remove the ignoral
// record.
$scope.config.IgnoredDevices = $scope.config.IgnoredDevices.filter(function (id) {
return id !== deviceCfg.DeviceID;
$scope.config.ignoredDevices = $scope.config.ignoredDevices.filter(function (id) {
return id !== deviceCfg.deviceID;
});
if (!$scope.editingSelf) {
for (var id in deviceCfg.selectedFolders) {
if (deviceCfg.selectedFolders[id]) {
var found = false;
for (i = 0; i < $scope.folders[id].Devices.length; i++) {
if ($scope.folders[id].Devices[i].DeviceID == deviceCfg.DeviceID) {
for (i = 0; i < $scope.folders[id].devices.length; i++) {
if ($scope.folders[id].devices[i].deviceID == deviceCfg.deviceID) {
found = true;
break;
}
}
if (!found) {
$scope.folders[id].Devices.push({
DeviceID: deviceCfg.DeviceID
$scope.folders[id].devices.push({
deviceID: deviceCfg.deviceID
});
}
} else {
$scope.folders[id].Devices = $scope.folders[id].Devices.filter(function (n) {
return n.DeviceID != deviceCfg.DeviceID;
$scope.folders[id].devices = $scope.folders[id].devices.filter(function (n) {
return n.deviceID != deviceCfg.deviceID;
});
}
}
@ -825,14 +824,14 @@ angular.module('syncthing.core')
};
$scope.ignoreRejectedDevice = function (device) {
$scope.config.IgnoredDevices.push(device);
$scope.config.ignoredDevices.push(device);
$scope.saveConfig();
$scope.dismissDeviceRejection(device);
};
$scope.otherDevices = function () {
return $scope.devices.filter(function (n) {
return n.DeviceID !== $scope.myID;
return n.deviceID !== $scope.myID;
});
};
@ -841,7 +840,7 @@ angular.module('syncthing.core')
for (i = 0; i < $scope.devices.length; i++) {
n = $scope.devices[i];
if (n.DeviceID === $scope.myID) {
if (n.deviceID === $scope.myID) {
return n;
}
}
@ -855,19 +854,19 @@ angular.module('syncthing.core')
$scope.errorList = function () {
return $scope.errors.filter(function (e) {
return e.Time > $scope.seenError;
return e.time > $scope.seenError;
});
};
$scope.clearErrors = function () {
$scope.seenError = $scope.errors[$scope.errors.length - 1].Time;
$scope.seenError = $scope.errors[$scope.errors.length - 1].time;
$http.post(urlbase + '/error/clear');
};
$scope.friendlyDevices = function (str) {
for (var i = 0; i < $scope.devices.length; i++) {
var cfg = $scope.devices[i];
str = str.replace(cfg.DeviceID, $scope.deviceName(cfg));
str = str.replace(cfg.deviceID, $scope.deviceName(cfg));
}
return str;
};
@ -878,7 +877,7 @@ angular.module('syncthing.core')
$scope.directoryList = [];
$scope.$watch('currentFolder.Path', function (newvalue) {
$scope.$watch('currentFolder.path', function (newvalue) {
$http.get(urlbase + '/autocomplete/directory', {
params: { current: newvalue }
}).success(function (data) {
@ -888,25 +887,25 @@ angular.module('syncthing.core')
$scope.editFolder = function (folderCfg) {
$scope.currentFolder = angular.copy(folderCfg);
if ($scope.currentFolder.Path.slice(-1) == $scope.system.pathSeparator) {
$scope.currentFolder.Path = $scope.currentFolder.Path.slice(0, -1);
if ($scope.currentFolder.path.slice(-1) == $scope.system.pathSeparator) {
$scope.currentFolder.path = $scope.currentFolder.path.slice(0, -1);
}
$scope.currentFolder.selectedDevices = {};
$scope.currentFolder.Devices.forEach(function (n) {
$scope.currentFolder.selectedDevices[n.DeviceID] = true;
$scope.currentFolder.devices.forEach(function (n) {
$scope.currentFolder.selectedDevices[n.deviceID] = true;
});
if ($scope.currentFolder.Versioning && $scope.currentFolder.Versioning.Type === "simple") {
if ($scope.currentFolder.versioning && $scope.currentFolder.versioning.type === "simple") {
$scope.currentFolder.simpleFileVersioning = true;
$scope.currentFolder.FileVersioningSelector = "simple";
$scope.currentFolder.simpleKeep = +$scope.currentFolder.Versioning.Params.keep;
} else if ($scope.currentFolder.Versioning && $scope.currentFolder.Versioning.Type === "staggered") {
$scope.currentFolder.fileVersioningSelector = "simple";
$scope.currentFolder.simpleKeep = +$scope.currentFolder.versioning.params.keep;
} else if ($scope.currentFolder.versioning && $scope.currentFolder.versioning.type === "staggered") {
$scope.currentFolder.staggeredFileVersioning = true;
$scope.currentFolder.FileVersioningSelector = "staggered";
$scope.currentFolder.staggeredMaxAge = Math.floor(+$scope.currentFolder.Versioning.Params.maxAge / 86400);
$scope.currentFolder.staggeredCleanInterval = +$scope.currentFolder.Versioning.Params.cleanInterval;
$scope.currentFolder.staggeredVersionsPath = $scope.currentFolder.Versioning.Params.versionsPath;
$scope.currentFolder.fileVersioningSelector = "staggered";
$scope.currentFolder.staggeredMaxAge = Math.floor(+$scope.currentFolder.versioning.params.maxAge / 86400);
$scope.currentFolder.staggeredCleanInterval = +$scope.currentFolder.versioning.params.cleanInterval;
$scope.currentFolder.staggeredVersionsPath = $scope.currentFolder.versioning.params.versionsPath;
} else {
$scope.currentFolder.FileVersioningSelector = "none";
$scope.currentFolder.fileVersioningSelector = "none";
}
$scope.currentFolder.simpleKeep = $scope.currentFolder.simpleKeep || 5;
$scope.currentFolder.staggeredCleanInterval = $scope.currentFolder.staggeredCleanInterval || 3600;
@ -928,8 +927,8 @@ angular.module('syncthing.core')
$scope.currentFolder = {
selectedDevices: {}
};
$scope.currentFolder.RescanIntervalS = 60;
$scope.currentFolder.FileVersioningSelector = "none";
$scope.currentFolder.rescanIntervalS = 60;
$scope.currentFolder.fileVersioningSelector = "none";
$scope.currentFolder.simpleKeep = 5;
$scope.currentFolder.staggeredMaxAge = 365;
$scope.currentFolder.staggeredCleanInterval = 3600;
@ -947,8 +946,8 @@ angular.module('syncthing.core')
};
$scope.currentFolder.selectedDevices[device] = true;
$scope.currentFolder.RescanIntervalS = 60;
$scope.currentFolder.FileVersioningSelector = "none";
$scope.currentFolder.rescanIntervalS = 60;
$scope.currentFolder.fileVersioningSelector = "none";
$scope.currentFolder.simpleKeep = 5;
$scope.currentFolder.staggeredMaxAge = 365;
$scope.currentFolder.staggeredCleanInterval = 3600;
@ -959,10 +958,10 @@ angular.module('syncthing.core')
};
$scope.shareFolderWithDevice = function (folder, device) {
$scope.folders[folder].Devices.push({
DeviceID: device
$scope.folders[folder].devices.push({
deviceID: device
});
$scope.config.Folders = folderList($scope.folders);
$scope.config.folders = folderList($scope.folders);
$scope.saveConfig();
$scope.dismissFolderRejection(folder, device);
};
@ -972,19 +971,19 @@ angular.module('syncthing.core')
$('#editFolder').modal('hide');
folderCfg = $scope.currentFolder;
folderCfg.Devices = [];
folderCfg.devices = [];
folderCfg.selectedDevices[$scope.myID] = true;
for (var deviceID in folderCfg.selectedDevices) {
if (folderCfg.selectedDevices[deviceID] === true) {
folderCfg.Devices.push({
DeviceID: deviceID
folderCfg.devices.push({
deviceID: deviceID
});
}
}
delete folderCfg.selectedDevices;
if (folderCfg.FileVersioningSelector === "simple") {
folderCfg.Versioning = {
if (folderCfg.fileVersioningSelector === "simple") {
folderCfg.versioning = {
'Type': 'simple',
'Params': {
'keep': '' + folderCfg.simpleKeep
@ -992,10 +991,10 @@ angular.module('syncthing.core')
};
delete folderCfg.simpleFileVersioning;
delete folderCfg.simpleKeep;
} else if (folderCfg.FileVersioningSelector === "staggered") {
folderCfg.Versioning = {
'Type': 'staggered',
'Params': {
} else if (folderCfg.fileVersioningSelector === "staggered") {
folderCfg.versioning = {
'type': 'staggered',
'params': {
'maxAge': '' + (folderCfg.staggeredMaxAge * 86400),
'cleanInterval': '' + folderCfg.staggeredCleanInterval,
'versionsPath': '' + folderCfg.staggeredVersionsPath
@ -1007,11 +1006,11 @@ angular.module('syncthing.core')
delete folderCfg.staggeredVersionsPath;
} else {
delete folderCfg.Versioning;
delete folderCfg.versioning;
}
$scope.folders[folderCfg.ID] = folderCfg;
$scope.config.Folders = folderList($scope.folders);
$scope.folders[folderCfg.id] = folderCfg;
$scope.config.folders = folderList($scope.folders);
$scope.saveConfig();
};
@ -1022,9 +1021,9 @@ angular.module('syncthing.core')
$scope.sharesFolder = function (folderCfg) {
var names = [];
folderCfg.Devices.forEach(function (device) {
if (device.DeviceID != $scope.myID) {
names.push($scope.deviceName($scope.findDevice(device.DeviceID)));
folderCfg.devices.forEach(function (device) {
if (device.deviceID != $scope.myID) {
names.push($scope.deviceName($scope.findDevice(device.deviceID)));
}
});
names.sort();
@ -1034,9 +1033,9 @@ angular.module('syncthing.core')
$scope.deviceFolders = function (deviceCfg) {
var folders = [];
for (var folderID in $scope.folders) {
var devices = $scope.folders[folderID].Devices
var devices = $scope.folders[folderID].devices
for (var i = 0; i < devices.length; i++) {
if (devices[i].DeviceID == deviceCfg.DeviceID) {
if (devices[i].deviceID == deviceCfg.deviceID) {
folders.push(folderID);
break;
}
@ -1053,8 +1052,8 @@ angular.module('syncthing.core')
return;
}
delete $scope.folders[$scope.currentFolder.ID];
$scope.config.Folders = folderList($scope.folders);
delete $scope.folders[$scope.currentFolder.id];
$scope.config.folders = folderList($scope.folders);
$scope.saveConfig();
};
@ -1065,7 +1064,7 @@ angular.module('syncthing.core')
}
$('#editIgnoresButton').attr('disabled', 'disabled');
$http.get(urlbase + '/ignores?folder=' + encodeURIComponent($scope.currentFolder.ID))
$http.get(urlbase + '/ignores?folder=' + encodeURIComponent($scope.currentFolder.id))
.success(function (data) {
data.ignore = data.ignore || [];
@ -1092,13 +1091,13 @@ angular.module('syncthing.core')
return;
}
$http.post(urlbase + '/ignores?folder=' + encodeURIComponent($scope.currentFolder.ID), {
$http.post(urlbase + '/ignores?folder=' + encodeURIComponent($scope.currentFolder.id), {
ignore: $('#editIgnores textarea').val().split('\n')
});
};
$scope.setAPIKey = function (cfg) {
cfg.APIKey = randomString(32);
cfg.apiKey = randomString(32);
};
$scope.showURPreview = function () {
@ -1109,13 +1108,13 @@ angular.module('syncthing.core')
};
$scope.acceptUR = function () {
$scope.config.Options.URAccepted = 1000; // Larger than the largest existing report version
$scope.config.options.urAccepted = 1000; // Larger than the largest existing report version
$scope.saveConfig();
$('#ur').modal('hide');
};
$scope.declineUR = function () {
$scope.config.Options.URAccepted = -1;
$scope.config.options.urAccepted = -1;
$scope.saveConfig();
$('#ur').modal('hide');
};
@ -1133,11 +1132,11 @@ angular.module('syncthing.core')
var fDelete = 4096;
var fDirectory = 16384;
if ((file.Flags & (fDelete + fDirectory)) === fDelete + fDirectory) {
if ((file.flags & (fDelete + fDirectory)) === fDelete + fDirectory) {
return 'rmdir';
} else if ((file.Flags & fDelete) === fDelete) {
} else if ((file.flags & fDelete) === fDelete) {
return 'rm';
} else if ((file.Flags & fDirectory) === fDirectory) {
} else if ((file.flags & fDirectory) === fDirectory) {
return 'touch';
} else {
return 'sync';

File diff suppressed because one or more lines are too long

View File

@ -39,12 +39,12 @@ var l = logger.DefaultLogger
const CurrentVersion = 9
type Configuration struct {
Version int `xml:"version,attr"`
Folders []FolderConfiguration `xml:"folder"`
Devices []DeviceConfiguration `xml:"device"`
GUI GUIConfiguration `xml:"gui"`
Options OptionsConfiguration `xml:"options"`
IgnoredDevices []protocol.DeviceID `xml:"ignoredDevice"`
Version int `xml:"version,attr" json:"version"`
Folders []FolderConfiguration `xml:"folder" json:"folders"`
Devices []DeviceConfiguration `xml:"device" json:"devices"`
GUI GUIConfiguration `xml:"gui" json:"gui"`
Options OptionsConfiguration `xml:"options" json:"options"`
IgnoredDevices []protocol.DeviceID `xml:"ignoredDevice" json:"ignoredDevices"`
XMLName xml.Name `xml:"configuration" json:"-"`
OriginalVersion int `xml:"-" json:"-"` // The version we read from disk, before any conversion
@ -53,19 +53,19 @@ type Configuration struct {
}
type FolderConfiguration struct {
ID string `xml:"id,attr"`
Path string `xml:"path,attr"`
Devices []FolderDeviceConfiguration `xml:"device"`
ReadOnly bool `xml:"ro,attr"`
RescanIntervalS int `xml:"rescanIntervalS,attr" default:"60"`
IgnorePerms bool `xml:"ignorePerms,attr"`
Versioning VersioningConfiguration `xml:"versioning"`
LenientMtimes bool `xml:"lenientMtimes"`
Copiers int `xml:"copiers" default:"1"` // This defines how many files are handled concurrently.
Pullers int `xml:"pullers" default:"16"` // Defines how many blocks are fetched at the same time, possibly between separate copier routines.
Hashers int `xml:"hashers" default:"0"` // Less than one sets the value to the number of cores. These are CPU bound due to hashing.
ID string `xml:"id,attr" json:"id"`
Path string `xml:"path,attr" json:"path"`
Devices []FolderDeviceConfiguration `xml:"device" json:"devices"`
ReadOnly bool `xml:"ro,attr" json:"readOnly"`
RescanIntervalS int `xml:"rescanIntervalS,attr" json:"rescanIntervalS" default:"60"`
IgnorePerms bool `xml:"ignorePerms,attr" json:"ignorePerms"`
Versioning VersioningConfiguration `xml:"versioning" json:"versioning"`
LenientMtimes bool `xml:"lenientMtimes" json:"lenientMTimes"`
Copiers int `xml:"copiers" json:"copiers" default:"1"` // This defines how many files are handled concurrently.
Pullers int `xml:"pullers" json:"pullers" default:"16"` // Defines how many blocks are fetched at the same time, possibly between separate copier routines.
Hashers int `xml:"hashers" json:"hashers" default:"0"` // Less than one sets the value to the number of cores. These are CPU bound due to hashing.
Invalid string `xml:"-"` // Set at runtime when there is an error, not saved
Invalid string `xml:"-" json:"invalid"` // Set at runtime when there is an error, not saved
deviceIDs []protocol.DeviceID
@ -105,8 +105,8 @@ func (f *FolderConfiguration) DeviceIDs() []protocol.DeviceID {
}
type VersioningConfiguration struct {
Type string `xml:"type,attr"`
Params map[string]string
Type string `xml:"type,attr" json:"type"`
Params map[string]string `json:"params"`
}
type InternalVersioningConfiguration struct {
@ -146,44 +146,44 @@ func (c *VersioningConfiguration) UnmarshalXML(d *xml.Decoder, start xml.StartEl
}
type DeviceConfiguration struct {
DeviceID protocol.DeviceID `xml:"id,attr"`
Name string `xml:"name,attr,omitempty"`
Addresses []string `xml:"address,omitempty"`
Compression protocol.Compression `xml:"compression,attr"`
CertName string `xml:"certName,attr,omitempty"`
Introducer bool `xml:"introducer,attr"`
DeviceID protocol.DeviceID `xml:"id,attr" json:"deviceID"`
Name string `xml:"name,attr,omitempty" json:"name"`
Addresses []string `xml:"address,omitempty" json:"addresses"`
Compression protocol.Compression `xml:"compression,attr" json:"compression"`
CertName string `xml:"certName,attr,omitempty" json:"certName"`
Introducer bool `xml:"introducer,attr" json:"introducer"`
}
type FolderDeviceConfiguration struct {
DeviceID protocol.DeviceID `xml:"id,attr"`
DeviceID protocol.DeviceID `xml:"id,attr" json:"deviceID"`
Deprecated_Name string `xml:"name,attr,omitempty" json:"-"`
Deprecated_Addresses []string `xml:"address,omitempty" json:"-"`
}
type OptionsConfiguration struct {
ListenAddress []string `xml:"listenAddress" default:"0.0.0.0:22000"`
GlobalAnnServers []string `xml:"globalAnnounceServer" default:"udp4://announce.syncthing.net:22026, udp6://announce-v6.syncthing.net:22026"`
GlobalAnnEnabled bool `xml:"globalAnnounceEnabled" default:"true"`
LocalAnnEnabled bool `xml:"localAnnounceEnabled" default:"true"`
LocalAnnPort int `xml:"localAnnouncePort" default:"21025"`
LocalAnnMCAddr string `xml:"localAnnounceMCAddr" default:"[ff32::5222]:21026"`
MaxSendKbps int `xml:"maxSendKbps"`
MaxRecvKbps int `xml:"maxRecvKbps"`
ReconnectIntervalS int `xml:"reconnectionIntervalS" default:"60"`
StartBrowser bool `xml:"startBrowser" default:"true"`
UPnPEnabled bool `xml:"upnpEnabled" default:"true"`
UPnPLease int `xml:"upnpLeaseMinutes" default:"0"`
UPnPRenewal int `xml:"upnpRenewalMinutes" default:"30"`
URAccepted int `xml:"urAccepted"` // Accepted usage reporting version; 0 for off (undecided), -1 for off (permanently)
URUniqueID string `xml:"urUniqueID"` // Unique ID for reporting purposes, regenerated when UR is turned on.
RestartOnWakeup bool `xml:"restartOnWakeup" default:"true"`
AutoUpgradeIntervalH int `xml:"autoUpgradeIntervalH" default:"12"` // 0 for off
KeepTemporariesH int `xml:"keepTemporariesH" default:"24"` // 0 for off
CacheIgnoredFiles bool `xml:"cacheIgnoredFiles" default:"true"`
ProgressUpdateIntervalS int `xml:"progressUpdateIntervalS" default:"5"`
SymlinksEnabled bool `xml:"symlinksEnabled" default:"true"`
LimitBandwidthInLan bool `xml:"limitBandwidthInLan" default:"false"`
ListenAddress []string `xml:"listenAddress" json:"listenAddress" default:"0.0.0.0:22000"`
GlobalAnnServers []string `xml:"globalAnnounceServer" json:"globalAnnounceServers" json:"globalAnnounceServer" default:"udp4://announce.syncthing.net:22026, udp6://announce-v6.syncthing.net:22026"`
GlobalAnnEnabled bool `xml:"globalAnnounceEnabled" json:"globalAnnounceEnabled" default:"true"`
LocalAnnEnabled bool `xml:"localAnnounceEnabled" json:"localAnnounceEnabled" default:"true"`
LocalAnnPort int `xml:"localAnnouncePort" json:"localAnnouncePort" default:"21025"`
LocalAnnMCAddr string `xml:"localAnnounceMCAddr" json:"localAnnounceMCAddr" default:"[ff32::5222]:21026"`
MaxSendKbps int `xml:"maxSendKbps" json:"maxSendKbps"`
MaxRecvKbps int `xml:"maxRecvKbps" json:"maxRecvKbps"`
ReconnectIntervalS int `xml:"reconnectionIntervalS" json:"reconnectionIntervalS" default:"60"`
StartBrowser bool `xml:"startBrowser" json:"startBrowser" default:"true"`
UPnPEnabled bool `xml:"upnpEnabled" json:"upnpEnabled" default:"true"`
UPnPLease int `xml:"upnpLeaseMinutes" json:"upnpLeaseMinutes" default:"0"`
UPnPRenewal int `xml:"upnpRenewalMinutes" json:"upnpRenewalMinutes" default:"30"`
URAccepted int `xml:"urAccepted" json:"urAccepted"` // Accepted usage reporting version; 0 for off (undecided), -1 for off (permanently)
URUniqueID string `xml:"urUniqueID" json:"urUniqueId"` // Unique ID for reporting purposes, regenerated when UR is turned on.
RestartOnWakeup bool `xml:"restartOnWakeup" json:"restartOnWakeup" default:"true"`
AutoUpgradeIntervalH int `xml:"autoUpgradeIntervalH" json:"autoUpgradeIntervalH" default:"12"` // 0 for off
KeepTemporariesH int `xml:"keepTemporariesH" json:"keepTemporariesH" default:"24"` // 0 for off
CacheIgnoredFiles bool `xml:"cacheIgnoredFiles" json:"cacheIgnoredFiles" default:"true"`
ProgressUpdateIntervalS int `xml:"progressUpdateIntervalS" json:"progressUpdateIntervalS" default:"5"`
SymlinksEnabled bool `xml:"symlinksEnabled" json:"symlinksEnabled" default:"true"`
LimitBandwidthInLan bool `xml:"limitBandwidthInLan" json:"limitBandwidthInLan" default:"false"`
Deprecated_RescanIntervalS int `xml:"rescanIntervalS,omitempty" json:"-"`
Deprecated_UREnabled bool `xml:"urEnabled,omitempty" json:"-"`
@ -194,12 +194,12 @@ type OptionsConfiguration struct {
}
type GUIConfiguration struct {
Enabled bool `xml:"enabled,attr" default:"true"`
Address string `xml:"address" default:"127.0.0.1:8080"`
User string `xml:"user,omitempty"`
Password string `xml:"password,omitempty"`
UseTLS bool `xml:"tls,attr"`
APIKey string `xml:"apikey,omitempty"`
Enabled bool `xml:"enabled,attr" json:"enabled" default:"true"`
Address string `xml:"address" json:"address" default:"127.0.0.1:8080"`
User string `xml:"user,omitempty" json:"user"`
Password string `xml:"password,omitempty" json:"password"`
UseTLS bool `xml:"tls,attr" json:"useTLS"`
APIKey string `xml:"apikey,omitempty" json:"apiKey"`
}
func New(myID protocol.DeviceID) Configuration {

View File

@ -18,6 +18,7 @@ package model
import (
"bufio"
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"io"
@ -241,6 +242,16 @@ type ConnectionInfo struct {
ClientVersion string
}
func (info ConnectionInfo) MarshalJSON() ([]byte, error) {
return json.Marshal(map[string]interface{}{
"at": info.At,
"inBytesTotal": info.InBytesTotal,
"outBytesTotal": info.OutBytesTotal,
"address": info.Address,
"clientVersion": info.ClientVersion,
})
}
// ConnectionStats returns a map with connection statistics for each connected device.
func (m *Model) ConnectionStats() map[string]ConnectionInfo {
type remoteAddrer interface {

View File

@ -49,14 +49,14 @@ type sharedPullerState struct {
// A momentary state representing the progress of the puller
type pullerProgress struct {
Total int
Reused int
CopiedFromOrigin int
CopiedFromElsewhere int
Pulled int
Pulling int
BytesDone int64
BytesTotal int64
Total int `json:"total"`
Reused int `json:"reused"`
CopiedFromOrigin int `json:"copiedFromOrigin"`
CopiedFromElsewhere int `json:"copiedFromElsewhere"`
Pulled int `json:"pulled"`
Pulling int `json:"pulling"`
BytesDone int64 `json:"bytesDone"`
BytesTotal int64 `json:"bytesTotal"`
}
// A lockedWriterAt synchronizes WriteAt calls with an external mutex.

View File

@ -24,7 +24,7 @@ import (
)
type DeviceStatistics struct {
LastSeen time.Time
LastSeen time.Time `json:"lastSeen"`
}
type DeviceStatisticsReference struct {

View File

@ -23,7 +23,7 @@ import (
)
type FolderStatistics struct {
LastFile LastFile
LastFile LastFile `json:"lastFile"`
}
type FolderStatisticsReference struct {
@ -32,8 +32,8 @@ type FolderStatisticsReference struct {
}
type LastFile struct {
At time.Time
Filename string
At time.Time `json:"at"`
Filename string `json:"filename"`
}
func NewFolderStatisticsReference(ldb *leveldb.DB, folder string) *FolderStatisticsReference {