mirror of
https://github.com/octoleo/syncthing.git
synced 2024-12-22 19:08:58 +00:00
Merge pull request #1465 from pascalj/lowercase-api
Use lowerCamelCase for the JSON API (fixes #1338)
This commit is contained in:
commit
36c93b755a
@ -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
|
||||
|
186
gui/index.html
186
gui/index.html
@ -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> <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">◼</span></span>
|
||||
<span ng-switch-when="unshared"><span class="hidden-xs" translate>Unshared</span><span class="visible-xs">◼</span></span>
|
||||
@ -187,7 +187,7 @@
|
||||
<span ng-switch-when="idle"><span class="hidden-xs" translate>Up to Date</span><span class="visible-xs">◼</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> <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> <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> <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> <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> <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> <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> <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> <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> <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> <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> <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> <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> <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> <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> <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> <span translate>Rescan</span></button>
|
||||
<button class="btn btn-sm btn-default" href="" ng-click="editFolder(folder)"><span class="glyphicon glyphicon-pencil"></span> <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> {{deviceName(deviceCfg)}}
|
||||
<identicon data-value="deviceCfg.deviceID"></identicon> {{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> <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> <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> <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> {{deviceName(deviceCfg)}}
|
||||
<identicon data-value="deviceCfg.deviceID"></identicon> {{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">◼</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">◼</span></span>
|
||||
<span ng-switch-when="unused"><span class="hidden-xs" translate>Unused</span><span class="visible-xs">◼</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> <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> <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> <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> <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> <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> <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> <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> <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> <span translate>Save</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>
|
||||
@ -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"> </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"> </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>
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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.
|
||||
|
@ -24,7 +24,7 @@ import (
|
||||
)
|
||||
|
||||
type DeviceStatistics struct {
|
||||
LastSeen time.Time
|
||||
LastSeen time.Time `json:"lastSeen"`
|
||||
}
|
||||
|
||||
type DeviceStatisticsReference struct {
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user