mirror of
https://github.com/octoleo/syncthing.git
synced 2024-11-14 01:04:14 +00:00
734 lines
43 KiB
HTML
734 lines
43 KiB
HTML
<!DOCTYPE html>
|
|
<!--
|
|
// Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file).
|
|
// All rights reserved. Use of this source code is governed by an MIT-style
|
|
// license that can be found in the LICENSE file.
|
|
-->
|
|
<html lang="en" ng-app="syncthing" ng-controller="SyncthingCtrl" class="ng-cloak">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<meta name="description" content="">
|
|
<meta name="author" content="">
|
|
<link rel="shortcut icon" href="img/favicon.png">
|
|
|
|
<title>Syncthing | {{thisNodeName()}}</title>
|
|
<link href="bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
|
<link href="font/raleway.css" rel="stylesheet">
|
|
<link href="overrides.css" rel="stylesheet">
|
|
</head>
|
|
|
|
<body>
|
|
<div ng-controller="EventCtrl"></div>
|
|
|
|
<!-- Top bar -->
|
|
|
|
<nav class="navbar navbar-top navbar-default" role="navigation">
|
|
<div class="container">
|
|
<span class="navbar-brand"><img class="logo" src="img/logo-text-64.png" height="32" width="117"/></span>
|
|
<p class="navbar-text hidden-xs">{{thisNodeName()}}</p>
|
|
<ul class="nav navbar-nav navbar-right">
|
|
<li ng-if="upgradeInfo.newer">
|
|
<button type="button" class="btn navbar-btn btn-default" href="" ng-click="upgrade()">
|
|
<span class="glyphicon glyphicon-chevron-up"></span> 
|
|
<span translate translate-value-version="{{upgradeInfo.latest}}">Upgrade To {%version%}</span>
|
|
</button>
|
|
</li>
|
|
<li class="dropdown">
|
|
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><span translate>Edit</spanq <b class="caret"></b></a>
|
|
<ul class="dropdown-menu">
|
|
<li><a href="" ng-click="addRepo()"><span class="glyphicon glyphicon-hdd"></span> <span translate>Add Repository</span></a></li>
|
|
<li><a href="" ng-click="addNode()"><span class="glyphicon glyphicon-retweet"></span> <span translate>Add Node</span></a></li>
|
|
<li class="divider"></li>
|
|
<li><a href="" ng-click="editSettings()"><span class="glyphicon glyphicon-cog"></span> <span translate>Settings</span></a></li>
|
|
<li><a href="" ng-click="idNode()"><span class="glyphicon glyphicon-qrcode"></span> <span translate>Show ID</span></a></li>
|
|
<li class="divider"></li>
|
|
<li><a href="" ng-click="shutdown()"><span class="glyphicon glyphicon-off"></span> <span translate>Shutdown</span></a></li>
|
|
<li><a href="" ng-click="restart()"><span class="glyphicon glyphicon-refresh"></span> <span translate>Restart</span></a></li>
|
|
<li class="divider"></li>
|
|
<li><a href="" ng-click="about()"><span class="glyphicon glyphicon-heart-empty"></span> <span translate>About</span></a></li>
|
|
</ul>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</nav>
|
|
|
|
<div class="container">
|
|
|
|
<!-- First row, only shown if necessary; Restart warning -->
|
|
|
|
<div ng-if="!configInSync" class="row">
|
|
<div class="col-md-12">
|
|
<div class="panel panel-warning">
|
|
<div class="panel-heading"><h3 translate class="panel-title">Restart Needed</h3></div>
|
|
<div class="panel-body">
|
|
<p translate>The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.</p>
|
|
</div>
|
|
<div class="panel-footer">
|
|
<button type="button" class="btn btn-sm btn-default pull-right" ng-click="restart()"><span class="glyphicon glyphicon-refresh"></span> <span translate>Restart</span></button>
|
|
<div class="clearfix"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- First regular row -->
|
|
|
|
<div class="row">
|
|
|
|
<!-- Repository list (top left) -->
|
|
|
|
<div class="col-md-6">
|
|
<div class="panel-group" id="repositories">
|
|
<div class="panel panel-{{repoClass(repo.ID)}}" ng-repeat="repo in repoList()">
|
|
<div class="panel-heading">
|
|
<h3 class="panel-title">
|
|
<a data-toggle="collapse" data-parent="#repositories" href="#repo-{{$index}}">
|
|
<span class="glyphicon glyphicon-hdd"></span> {{repo.ID}}
|
|
<span class="pull-right hidden-xs" ng-switch="repoStatus(repo.ID)">
|
|
<span translate ng-switch-when="unknown">Unknown</span>
|
|
<span translate ng-switch-when="stopped">Stopped</span>
|
|
<span translate ng-switch-when="scanning">Scanning</span>
|
|
<span ng-switch-when="syncing">
|
|
<span translate>Syncing</span>
|
|
({{syncPercentage(repo.ID)}}%)
|
|
</span>
|
|
<span ng-switch-when="idle">
|
|
<span translate>Idle</span>
|
|
({{syncPercentage(repo.ID)}}%)
|
|
</span>
|
|
</span>
|
|
</a>
|
|
</h3>
|
|
</div>
|
|
<div id="repo-{{$index}}" class="panel-collapse collapse">
|
|
<div class="panel-body">
|
|
<table class="table table-condensed table-striped">
|
|
<tbody>
|
|
<tr>
|
|
<th><span class="glyphicon glyphicon-tag"></span> <span translate>Repository ID</span></th>
|
|
<td class="text-right">{{repo.ID}}</td>
|
|
</tr>
|
|
<tr>
|
|
<th><span class="glyphicon glyphicon-folder-open"></span> <span translate>Folder</span></th>
|
|
<td class="text-right">{{repo.Directory}}</td>
|
|
</tr>
|
|
<tr ng-if="model[repo.ID].invalid">
|
|
<th><span class="glyphicon glyphicon-warning-sign"></span> <span translate>Error</span></th>
|
|
<td class="text-right">{{model[repo.ID].invalid}}</td>
|
|
</tr>
|
|
<tr>
|
|
<th><span class="glyphicon glyphicon-globe"></span> <span translate>Global Repository</span></th>
|
|
<td class="text-right">{{model[repo.ID].globalFiles | alwaysNumber}} <span translate>items</span>, ~{{model[repo.ID].globalBytes | binary}}B</td>
|
|
</tr>
|
|
<tr>
|
|
<th><span class="glyphicon glyphicon-home"></span> <span translate>Local Repository</span></th>
|
|
<td class="text-right">{{model[repo.ID].localFiles | alwaysNumber}} <span translate>items</span>, ~{{model[repo.ID].localBytes | binary}}B</td>
|
|
</tr>
|
|
<tr>
|
|
<th><span class="glyphicon glyphicon-cloud-download"></span> <span translate>Out Of Sync</span></th>
|
|
<td class="text-right">
|
|
<a ng-if="model[repo.ID].needFiles > 0" ng-click="showNeed(repo.ID)" href="">{{model[repo.ID].needFiles | alwaysNumber}} <span translate>items</span>, ~{{model[repo.ID].needBytes | binary}}B</a>
|
|
<span ng-if="model[repo.ID].needFiles == 0">0 <span translate>items</span>, 0 B</span>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th><span class="glyphicon glyphicon-lock"></span> <span translate>Master Repo</span></th>
|
|
<td class="text-right">
|
|
<span translate ng-if="repo.ReadOnly">Yes</span>
|
|
<span translate ng-if="!repo.ReadOnly">No</span>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th><span class="glyphicon glyphicon-unchecked"></span> <span translate>Ignore Permissions</span></th>
|
|
<td class="text-right">
|
|
<span translate ng-if="repo.IgnorePerms">Yes</span>
|
|
<span translate ng-if="!repo.IgnorePerms">No</span>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th><span class="glyphicon glyphicon-refresh"></span> <span translate>Rescan Interval</span></th>
|
|
<td class="text-right">{{repo.RescanIntervalS}} s</td>
|
|
</tr>
|
|
<tr>
|
|
<th><span class="glyphicon glyphicon-share-alt"></span> <span translate>Shared With</span></th>
|
|
<td class="text-right">{{sharesRepo(repo)}}</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<span class="pull-right">
|
|
<a class="btn btn-sm btn-default" href="" ng-show="repoStatus(repo.ID) == 'idle'" ng-click="rescanRepo(repo.ID)"><span class="glyphicon glyphicon-refresh"></span> <span translate>Rescan</span></a>
|
|
<a class="btn btn-sm btn-primary" href="" ng-click="editRepo(repo)"><span class="glyphicon glyphicon-pencil"></span> <span translate>Edit</span></a>
|
|
<a class="btn btn-sm btn-danger" ng-if="repo.ReadOnly && model[repo.ID].needFiles > 0" ng-click="override(repo.ID)" href=""><span class="glyphicon glyphicon-upload"></span> <span translate>Override Changes</span></a>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Node list (top right) -->
|
|
|
|
<div class="col-md-6">
|
|
<div class="panel-group" id="nodes">
|
|
<div class="panel panel-default" ng-repeat="nodeCfg in [thisNode()]">
|
|
<div class="panel-heading">
|
|
<h3 class="panel-title">
|
|
<a data-toggle="collapse" data-parent="#nodes" href="#node-this"><span class="glyphicon glyphicon-home"></span> {{nodeName(nodeCfg)}}</a>
|
|
</h3>
|
|
</div>
|
|
<div id="node-this" class="panel-collapse collapse in">
|
|
<div class="panel-body">
|
|
<table class="table table-condensed table-striped">
|
|
<tbody>
|
|
<tr>
|
|
<th><span class="glyphicon glyphicon-th"></span> <span translate>RAM Utilization</span></th>
|
|
<td class="text-right">{{system.sys | binary}}B</td>
|
|
</tr>
|
|
<tr>
|
|
<th><span class="glyphicon glyphicon-tasks"></span> <span translate>CPU Utilization</span></th>
|
|
<td class="text-right">{{system.cpuPercent | alwaysNumber | natural:1}}%</td>
|
|
</tr>
|
|
<tr>
|
|
<th><span class="glyphicon glyphicon-cloud-download"></span> <span translate>Download Rate</span></th>
|
|
<td class="text-right">{{connections['total'].inbps | metric}}bps ({{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 | metric}}bps ({{connections['total'].OutBytesTotal | binary}}B)</td>
|
|
</tr>
|
|
<tr ng-if="system.extAnnounceOK != undefined">
|
|
<th><span class="glyphicon glyphicon-bullhorn"></span> <span translate>Announce Server</span></th>
|
|
<td class="text-right">
|
|
<span class="data text-success" ng-if="system.extAnnounceOK"><span translate>Online</span></span>
|
|
<span class="data text-danger" ng-if="!system.extAnnounceOK"><span translate>Offline</span></span>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th><span class="glyphicon glyphicon-tag"></span> <span translate>Version</span></th>
|
|
<td class="text-right">{{version}}</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<span class="pull-right"><a class="btn btn-sm btn-primary" href="" ng-click="editNode(nodeCfg)"><span class="glyphicon glyphicon-pencil"></span> <span translate>Edit</span></a></span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="panel panel-{{nodeClass(nodeCfg)}}" ng-repeat="nodeCfg in otherNodes()">
|
|
<div class="panel-heading">
|
|
<h3 class="panel-title">
|
|
<a data-toggle="collapse" data-parent="#nodes" href="#node-{{$index}}">
|
|
<span class="glyphicon glyphicon-retweet"></span>
|
|
{{nodeName(nodeCfg)}}
|
|
<span class="pull-right hidden-xs">
|
|
<span ng-if="connections[nodeCfg.NodeID] && completion[nodeCfg.NodeID]._total == 100">
|
|
<span translate>Up to Date</span> (100%)
|
|
</span>
|
|
<span ng-if="connections[nodeCfg.NodeID] && completion[nodeCfg.NodeID]._total < 100">
|
|
<span translate>Syncing</span> ({{completion[nodeCfg.NodeID]._total | number:0}}%)
|
|
</span>
|
|
<span translate ng-if="!connections[nodeCfg.NodeID]">Disconnected</span>
|
|
</span>
|
|
</a>
|
|
</h3>
|
|
</div>
|
|
<div id="node-{{$index}}" class="panel-collapse collapse">
|
|
<div class="panel-body">
|
|
<table class="table table-condensed table-striped">
|
|
<tbody>
|
|
<tr>
|
|
<th><span class="glyphicon glyphicon-link"></span> <span translate>Address</span></th>
|
|
<td class="text-right">{{nodeAddr(nodeCfg)}}</td>
|
|
</tr>
|
|
<tr>
|
|
<th><span class="glyphicon glyphicon-comment"></span> <span translate>Synchronization</span></th>
|
|
<td class="text-right">{{completion[nodeCfg.NodeID]._total | alwaysNumber | number:0}}%</td>
|
|
</tr>
|
|
<tr>
|
|
<th><span class="glyphicon glyphicon-compressed"></span> <span translate>Use Compression</span></th>
|
|
<td translate ng-if="nodeCfg.Compression" class="text-right">Yes</td>
|
|
<td translate ng-if="!nodeCfg.Compression" class="text-right">No</td>
|
|
</tr>
|
|
<tr>
|
|
<th><span class="glyphicon glyphicon-cloud-download"></span> <span translate>Download Rate</span></th>
|
|
<td class="text-right">{{connections[nodeCfg.NodeID].inbps | metric}}bps ({{connections[nodeCfg.NodeID].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[nodeCfg.NodeID].outbps | metric}}bps ({{connections[nodeCfg.NodeID].OutBytesTotal | binary}}B)</td>
|
|
</tr>
|
|
<tr>
|
|
<th><span class="glyphicon glyphicon-tag"></span> <span translate>Version</span></th>
|
|
<td class="text-right">{{nodeVer(nodeCfg)}}</td>
|
|
</tr>
|
|
<tr ng-if="!connections[nodeCfg.NodeID]">
|
|
<th><span class="glyphicon glyphicon-eye-open"></span> <span translate>Last seen</span></th>
|
|
<td translate ng-if="stats[nodeCfg.NodeID].LastSeen.indexOf('1970') > -1" class="text-right">Never</td>
|
|
<td ng-if="stats[nodeCfg.NodeID].LastSeen.indexOf('1970') < 0" class="text-right">{{stats[nodeCfg.NodeID].LastSeen | date:"yyyy-MM-dd HH:mm"}}</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<span class="pull-right"><a class="btn btn-sm btn-primary" href="" ng-click="editNode(nodeCfg)"><span class="glyphicon glyphicon-pencil"></span> <span translate>Edit</span></a></span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div> <!-- /row -->
|
|
|
|
<!-- Errors -->
|
|
|
|
<div ng-if="errorList().length > 0" class="row">
|
|
<div class="col-md-12">
|
|
<div class="panel panel-warning">
|
|
<div class="panel-heading"><h3 class="panel-title"><span translate>Notice</span></h3></div>
|
|
<div class="panel-body">
|
|
<p ng-repeat="err in errorList()"><small>{{err.Time | date:"H:mm:ss"}}:</small> {{friendlyNodes(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>
|
|
<div class="clearfix"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div> <!-- /container -->
|
|
|
|
<!-- Bottom bar -->
|
|
|
|
<nav class="navbar navbar-default navbar-fixed-bottom hidden-xs">
|
|
<div class="container">
|
|
<ul class="nav navbar-nav">
|
|
<li><a class="navbar-link" href="http://discourse.syncthing.net/"><span translate>Support / Forum</span></a></li>
|
|
<li><a class="navbar-link" href="https://github.com/syncthing/syncthing/releases"><span translate>Latest Release</span></a></li>
|
|
<li><a class="navbar-link" href="http://discourse.syncthing.net/category/documentation"><span translate>Documentation</span></a></li>
|
|
<li><a class="navbar-link" href="https://github.com/syncthing/syncthing/issues"><span translate>Bugs</span></a></li>
|
|
<li><a class="navbar-link" href="https://github.com/syncthing/syncthing"><span translate>Source Code</span></a></li>
|
|
</ul>
|
|
</div>
|
|
</nav>
|
|
|
|
<!-- Network error modal -->
|
|
|
|
<modal id="networkError" status="danger" icon="exclamation-sign" title="{{'Connection Error' | translate}}">
|
|
<p translate>
|
|
Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…
|
|
</p>
|
|
</modal>
|
|
|
|
<!-- Restarting modal -->
|
|
|
|
<modal id="restarting" icon="refresh" title="{{'Restarting' | translate}}" status="info">
|
|
<p><span translate>Syncthing is restarting.</span> <span translate>Please wait</span>...</p>
|
|
</modal>
|
|
|
|
<!-- Upgrading modal -->
|
|
|
|
<modal id="upgrading" icon="refresh" title="{{'Upgrading' | translate}}" status="info">
|
|
<p><span translate>Syncthing is upgrading.</span> <span translate>Please wait</span>...</p>
|
|
</modal>
|
|
|
|
<!-- Shutdown modal -->
|
|
|
|
<modal id="shutdown" icon="off" status="success" title="Shutdown Complete">
|
|
<p translate>Syncthing has been shut down.</p>
|
|
</modal>
|
|
|
|
<!-- ID modal -->
|
|
|
|
<modal id="idqr" large="yes" status="info" close="yes" icon="qrcode" title="{{'Node Identification' | translate}} — {{nodeName(thisNode())}}">
|
|
<div class="well well-sm text-monospace text-center">{{myID}}</div>
|
|
<img ng-if="myID" class="center-block img-thumbnail" src="qr/?text={{myID}}"/>
|
|
</modal>
|
|
|
|
<!-- Node editor modal -->
|
|
|
|
<div id="editNode" class="modal fade" tabindex="-1">
|
|
<div class="modal-dialog modal-lg">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h4 translate ng-show="!editingExisting" class="modal-title">Add Node</h4>
|
|
<h4 translate ng-show="editingExisting" class="modal-title">Edit Node</h4>
|
|
</div>
|
|
<div class="modal-body">
|
|
<form role="form" name="nodeEditor">
|
|
<div class="form-group" ng-class="{'has-error': nodeEditor.nodeID.$invalid && nodeEditor.nodeID.$dirty}">
|
|
<label translate for="nodeID">Node ID</label>
|
|
<input ng-if="!editingExisting" name="nodeID" id="nodeID" class="form-control text-monospace" type="text" ng-model="currentNode.NodeID" required valid-nodeid></input>
|
|
<div ng-if="editingExisting" class="well well-sm text-monospace">{{currentNode.NodeID}}</div>
|
|
<p class="help-block">
|
|
<span translate ng-if="nodeEditor.nodeID.$valid || nodeEditor.nodeID.$pristine">The node ID to enter here can be found in the "Edit > Show ID" dialog on the other node. Spaces and dashes are optional (ignored).</span>
|
|
<span translate ng-show="!editingExisting && (nodeEditor.nodeID.$valid || nodeEditor.nodeID.$pristine)">When adding a new node, keep in mind that this node must be added on the other side too.</span>
|
|
<span translate ng-if="nodeEditor.nodeID.$error.required && nodeEditor.nodeID.$dirty">The node ID cannot be blank.</span>
|
|
<span translate ng-if="nodeEditor.nodeID.$error.validNodeid && nodeEditor.nodeID.$dirty">The entered node ID does not look valid. It should be a 52 or 56 character string consisting of letters and numbers, with spaces and dashes being optional.</span>
|
|
</p>
|
|
</div>
|
|
<div class="form-group">
|
|
<label translate for="name">Node Name</label>
|
|
<input placeholder="Home Server" id="name" class="form-control" type="text" ng-model="currentNode.Name"></input>
|
|
<p translate ng-if="currentNode.NodeID == myID" class="help-block">Shown instead of Node ID in the cluster status. Will be advertised to other nodes as an optional default name.</p>
|
|
<p translate ng-if="currentNode.NodeID != myID" class="help-block">Shown instead of Node ID in the cluster status. Will be updated to the name the node advertises if left empty.</p>
|
|
</div>
|
|
<div class="form-group">
|
|
<label translate for="addresses">Addresses</label>
|
|
<input placeholder="dynamic" ng-disabled="currentNode.NodeID == myID" id="addresses" class="form-control" type="text" ng-model="currentNode.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">
|
|
<div class="checkbox">
|
|
<label>
|
|
<input type="checkbox" ng-model="currentNode.Compression"> <span translate>Use Compression</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-primary" ng-click="saveNode()" ng-disabled="nodeEditor.$invalid"><span class="glyphicon glyphicon-ok"></span> <span translate>Save</span></button>
|
|
<button type="button" class="btn btn-default" data-dismiss="modal"><span class="glyphicon glyphicon-remove"></span> <span translate>Close</span></button>
|
|
<button ng-if="editingExisting && !editingSelf" type="button" class="btn btn-danger pull-left" ng-click="deleteNode()"><span class="glyphicon glyphicon-minus"></span> <span translate>Delete</span></button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Repo editor modal -->
|
|
|
|
<div id="editRepo" class="modal fade" tabindex="-1">
|
|
<div class="modal-dialog modal-lg">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h4 ng-show="!editingExisting" class="modal-title"><span translate>Add Repository</span></h4>
|
|
<h4 ng-show="editingExisting" class="modal-title"><span translate>Edit Repository</span></h4>
|
|
</div>
|
|
<div class="modal-body">
|
|
<form role="form" name="repoEditor">
|
|
<div class="row">
|
|
<div class="col-md-12">
|
|
<div class="form-group" ng-class="{'has-error': repoEditor.repoID.$invalid && repoEditor.repoID.$dirty}">
|
|
<label for="repoID"><span translate>Repository ID</span></label>
|
|
<input name="repoID" placeholder="documents" ng-disabled="editingExisting" id="repoID" class="form-control" type="text" ng-model="currentRepo.ID" required unique-repo ng-pattern="/^[a-zA-Z0-9-_.]{1,64}$/"></input>
|
|
<p class="help-block">
|
|
<span translate ng-if="repoEditor.repoID.$valid || repoEditor.repoID.$pristine">Short identifier for the repository. Must be the same on all cluster nodes.</span>
|
|
<span translate ng-if="repoEditor.repoID.$error.uniqueRepo">The repository ID must be unique.</span>
|
|
<span translate ng-if="repoEditor.repoID.$error.required && repoEditor.repoID.$dirty">The repository ID cannot be blank.</span>
|
|
<span translate ng-if="repoEditor.repoID.$error.pattern && repoEditor.repoID.$dirty">The repository ID must be a short identifier (64 characters or less) consisting of letters, numbers and the the dot (.), dash (-) and underscode (_) characters only.</span>
|
|
</p>
|
|
</div>
|
|
<div class="form-group" ng-class="{'has-error': repoEditor.repoPath.$invalid && repoEditor.repoPath.$dirty}">
|
|
<label translate for="repoPath">Repository Path</label>
|
|
<input name="repoPath" placeholder="~/Documents" ng-disabled="editingExisting" id="repoPath" class="form-control" type="text" ng-model="currentRepo.Directory" required></input>
|
|
<p class="help-block">
|
|
<span translate ng-if="repoEditor.repoPath.$valid || repoEditor.repoPath.$pristine">Path to the repository on the local computer. Will be created if it does not exist. The tilde character (~) can be used as a shortcut for</span> <code>{{system.tilde}}</code>.
|
|
<span translate ng-if="repoEditor.repoPath.$error.required && repoEditor.repoPath.$dirty">The repository path cannot be blank.</span>
|
|
</p>
|
|
</div>
|
|
<div class="form-group" ng-class="{'has-error': repoEditor.rescanIntervalS.$invalid && repoEditor.rescanIntervalS.$dirty}">
|
|
<label for="rescanIntervalS"><span translate>Rescan Interval</span> (s)</label>
|
|
<input name="rescanIntervalS" placeholder="60" id="rescanIntervalS" class="form-control" type="number" ng-model="currentRepo.RescanIntervalS" required min="5"></input>
|
|
<p class="help-block">
|
|
<span translate ng-if="!repoEditor.rescanIntervalS.$valid && repoEditor.rescanIntervalS.$dirty">The rescan interval must be at least 5 seconds.</span>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="form-group">
|
|
<div class="checkbox">
|
|
<label>
|
|
<input type="checkbox" ng-model="currentRepo.ReadOnly"> <span translate>Repository Master</span>
|
|
</label>
|
|
</div>
|
|
<p translate class="help-block">Files are protected from changes made on other nodes, but changes made on this node will be sent to the rest of the cluster.</p>
|
|
</div>
|
|
<div class="form-group">
|
|
<div class="checkbox">
|
|
<label>
|
|
<input type="checkbox" ng-model="currentRepo.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>
|
|
</div>
|
|
<div class="form-group">
|
|
<label translate for="nodes">Share With Nodes</label>
|
|
<div class="checkbox" ng-repeat="node in otherNodes()">
|
|
<label>
|
|
<input type="checkbox" ng-model="currentRepo.selectedNodes[node.NodeID]"> {{nodeName(node)}}
|
|
</label>
|
|
</div>
|
|
<p translate class="help-block">Select the nodes to share this repository with.</p>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-group">
|
|
<label translate>File Versioning</label>
|
|
<div class="radio">
|
|
<label>
|
|
<input type="radio" ng-model="currentRepo.FileVersioningSelector" value="none"> <span translate>No File Versioning</span>
|
|
</label>
|
|
</div>
|
|
<div class="radio">
|
|
<label>
|
|
<input type="radio" ng-model="currentRepo.FileVersioningSelector" value="simple"> <span translate>Simple File Versioning</span>
|
|
</label>
|
|
</div>
|
|
<div class="radio">
|
|
<label>
|
|
<input type="radio" ng-model="currentRepo.FileVersioningSelector" value="staggered"> <span translate>Staggered File Versioning</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="form-group" ng-if="currentRepo.FileVersioningSelector=='simple'" ng-class="{'has-error': repoEditor.simpleKeep.$invalid && repoEditor.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="currentRepo.simpleKeep" required min="1"></input>
|
|
<p class="help-block">
|
|
<span translate ng-if="repoEditor.simpleKeep.$valid || repoEditor.simpleKeep.$pristine">The number of old versions to keep, per file.</span>
|
|
<span translate ng-if="repoEditor.simpleKeep.$error.required && repoEditor.simpleKeep.$dirty">The number of versions must be a number and cannot be blank.</span>
|
|
<span translate ng-if="repoEditor.simpleKeep.$error.min && repoEditor.simpleKeep.$dirty">You must keep at least one version.</span>
|
|
</p>
|
|
</div>
|
|
<div class="form-group" ng-if="currentRepo.FileVersioningSelector=='staggered'" ng-class="{'has-error': repoEditor.staggeredMaxAge.$invalid && repoEditor.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>
|
|
<input name="staggeredMaxAge" id="staggeredMaxAge" class="form-control" type="number" ng-model="currentRepo.staggeredMaxAge" required></input>
|
|
<p class="help-block">
|
|
<span translate ng-if="repoEditor.staggeredMaxAge.$valid || repoEditor.staggeredMaxAge.$pristine">The maximum time to keep a version (in days, set to 0 to keep versions forever).</span>
|
|
<span translate ng-if="repoEditor.staggeredMaxAge.$error.required && repoEditor.staggeredMaxAge.$dirty">The maximum age must be a number and cannot be blank.</span>
|
|
</p>
|
|
</div>
|
|
<div class="form-group" ng-if="currentRepo.FileVersioningSelector == 'staggered'">
|
|
<label translate for="staggeredVersionsPath">Versions Path</label>
|
|
<input name="staggeredVersionsPath" placeholder="" id="staggeredVersionsPath" class="form-control" type="text" ng-model="currentRepo.staggeredVersionsPath"></input>
|
|
<p translate class="help-block">Path where versions should be stored (leave empty for the default .stversions folder in the repository).</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
<div translate ng-show="!editingExisting">When adding a new repository, keep in mind that the Repository ID is used to tie repositories together between nodes. They are case sensitive and must match exactly between all nodes.</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-primary" ng-click="saveRepo()" ng-disabled="repoEditor.$invalid"><span class="glyphicon glyphicon-ok"></span> <span translate>Save</span></button>
|
|
<button type="button" class="btn btn-default" data-dismiss="modal"><span class="glyphicon glyphicon-remove"></span> <span translate>Close</span></button>
|
|
<button ng-if="editingExisting" type="button" class="btn btn-danger pull-left" ng-click="deleteRepo()"><span class="glyphicon glyphicon-minus"></span> <span translate>Delete</span></button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Settings modal -->
|
|
|
|
<div id="settings" class="modal fade" tabindex="-1">
|
|
<div class="modal-dialog modal-lg">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h4 translate class="modal-title">Settings</h4>
|
|
</div>
|
|
<div class="modal-body">
|
|
<form role="form">
|
|
<div class="row">
|
|
|
|
<div class="col-md-6">
|
|
<div class="form-group">
|
|
<label translate for="ListenStr">Sync Protocol Listen Addresses</label>
|
|
<input id="ListenStr" class="form-control" type="text" ng-model="tmpOptions.ListenStr">
|
|
</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">
|
|
</div>
|
|
<!--
|
|
<div class="form-group">
|
|
<label translate for="ReconnectIntervalS">Reconnect Interval (s)</label>
|
|
<input id="ReconnectIntervalS" class="form-control" type="number" ng-model="tmpOptions.ReconnectIntervalS">
|
|
</div>
|
|
<div class="form-group">
|
|
<label translate for="ParallelRequests">Max Outstanding Requests</label>
|
|
<input id="ParallelRequests" class="form-control" type="number" ng-model="tmpOptions.ParallelRequests">
|
|
</div>
|
|
<div class="form-group">
|
|
<label translate for="MaxChangeKbps">Max File Change Rate (KiB/s)</label>
|
|
<input id="MaxChangeKbps" class="form-control" type="number" ng-model="tmpOptions.MaxChangeKbps">
|
|
</div>
|
|
-->
|
|
<div class="form-group">
|
|
<div class="checkbox">
|
|
<label>
|
|
<span translate>Enable UPnP</span> <input id="UPnPEnabled" type="checkbox" ng-model="tmpOptions.UPnPEnabled">
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<div class="checkbox">
|
|
<label>
|
|
<span translate>Local Discovery</span> <input id="LocalAnnEnabled" type="checkbox" ng-model="tmpOptions.LocalAnnEnabled">
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<div class="checkbox">
|
|
<label>
|
|
<span translate>Global Discovery</span> <input id="GlobalAnnEnabled" type="checkbox" ng-model="tmpOptions.GlobalAnnEnabled">
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label translate for="GlobalAnnServer">Global Discovery Server</label>
|
|
<input ng-disabled="!tmpOptions.GlobalAnnEnabled" id="GlobalAnnServer" class="form-control" type="text" ng-model="tmpOptions.GlobalAnnServer">
|
|
</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">
|
|
</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">
|
|
</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">
|
|
</div>
|
|
<div class="form-group">
|
|
<div class="checkbox">
|
|
<label>
|
|
<span translate>Use HTTPS for GUI</span> <input id="UseTLS" type="checkbox" ng-model="tmpGUI.UseTLS">
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<div class="checkbox">
|
|
<label>
|
|
<span translate>Start Browser</span> <input id="StartBrowser" type="checkbox" ng-model="tmpOptions.StartBrowser">
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<div class="checkbox">
|
|
<label>
|
|
<span translate>Anonymous Usage Reporting</span> <input id="UREnabled" type="checkbox" ng-model="tmpOptions.UREnabled">
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<hr />
|
|
|
|
<div class="form-group">
|
|
<label><span translate>API Key</span></label>
|
|
<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>
|
|
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-primary" ng-click="saveSettings()"><span class="glyphicon glyphicon-ok"></span> <span translate>Save</span></button>
|
|
<button type="button" class="btn btn-default" data-dismiss="modal"><span class="glyphicon glyphicon-remove"></span> <span translate>Close</span></button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Usage report modal -->
|
|
|
|
<div id="ur" class="modal fade" data-backdrop="static" data-keyboard="false" tabindex="-1">
|
|
<div class="modal-dialog modal-lg">
|
|
<div class="modal-content">
|
|
<div class="modal-header alert alert-success">
|
|
<h4 translate class="modal-title">Allow Anonymous Usage Reporting?</h4>
|
|
</div>
|
|
<div class="modal-body">
|
|
<p translate>The encrypted usage report is sent daily. It is used to track common platforms, repo sizes and app versions. If the reported data set is changed you will be prompted with this dialog again.</p>
|
|
<p translate translate-value-url="https://data.syncthing.net">The aggregated statistics are publicly available at {%url%}.</p>
|
|
<button translate type="button" class="btn btn-default" ng-show="!reportPreview" ng-click="showReportPreview()">Preview Usage Report</button>
|
|
<pre ng-if="reportPreview"><small>{{reportData | json}}</small></pre>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-success" ng-click="acceptUR()"><span class="glyphicon glyphicon-ok"></span> <span translate>Yes</span></button>
|
|
<button type="button" class="btn btn-danger" ng-click="declineUR()"><span class="glyphicon glyphicon-remove"></span> <span translate>No</span></button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Needed files modal -->
|
|
|
|
<modal id="needed" large="yes" status="info" icon="cloud-download" close="yes" title="Out of Sync Items">
|
|
<table class="table table-striped table-condensed">
|
|
<tr ng-repeat="f in needed" 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>
|
|
</tr>
|
|
</table>
|
|
</modal>
|
|
|
|
<!-- About modal -->
|
|
|
|
<modal id="about" large="yes" close="yes" status="info" title="About">
|
|
<h1 class="text-center"><img alt="Syncthing" title="Syncthing" src="img/logo-text-256.png" style="vertical-align: -16px" height="100" width="366"/><br/><small>{{version}}</small></h1>
|
|
<hr/>
|
|
|
|
<p translate>Copyright © 2014 Jakob Borg and the following Contributors:</p>
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<ul>
|
|
<li>Aaron Bieber</li>
|
|
<li>Andrew Dunham</li>
|
|
<li>Alexander Graf</li>
|
|
<li>Arthur Axel fREW Schmidt</li>
|
|
<li>Audrius Butkevicius</li>
|
|
<li>Ben Sidhom</li>
|
|
<li>Brandon Philips</li>
|
|
<li>Gilli Sigurdsson</li>
|
|
</ul>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<ul>
|
|
<li>James Patterson</li>
|
|
<li>Jens Diemer</li>
|
|
<li>Marcin Dziadus</li>
|
|
<li>Philippe Schommers</li>
|
|
<li>Ryan Sullivan</li>
|
|
<li>Tully Robinson</li>
|
|
<li>Veeti Paananen</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
<hr/>
|
|
|
|
<p translate>Syncthing includes the following software or portions thereof:</p>
|
|
<ul>
|
|
<li><a href="http://golang.org/">The Go Programming Language</a>, Copyright © 2012 The Go Authors.</li>
|
|
<li><a href="https://bitbucket.org/kardianos/osext">kardianos/osext</a>, Copyright © 2012 Daniel Theophanes.</li>
|
|
<li><a href="https://code.google.com/p/snappy-go/">snappy-go</a>, Copyright © 2011 The Snappy-Go Authors.</li>
|
|
<li><a href="https://github.com/golang/groupcache">groupcache/lru</a>, Copyright © 2013 Google Inc.</li>
|
|
<li><a href="https://github.com/juju/ratelimit">juju/ratelimit</a>, Copyright © 2014 Canonical Ltd.</li>
|
|
<li><a href="https://github.com/syndtr/goleveldb">syndtr/goleveldb</a>, Copyright © 2012, Suryandaru Triandana</li>
|
|
<li><a href="https://github.com/vitrun/qart">vitrun/qart</a>, Copyright © The Go Authors.</li>
|
|
<li><a href="https://angularjs.org/">AngularJS</a>, Copyright © 2010-2014 Google, Inc.</li>
|
|
<li><a href="http://getbootstrap.com/">Bootstrap</a>, Copyright © 2011-2014 Twitter, Inc.</li>
|
|
</ul>
|
|
</modal>
|
|
|
|
|
|
<script src="angular/angular.min.js"></script>
|
|
<script src="angular/angular-translate.min.js"></script>
|
|
<script src="angular/angular-translate-loader.min.js"></script>
|
|
<script src="jquery/jquery-2.0.3.min.js"></script>
|
|
<script src="bootstrap/js/bootstrap.min.js"></script>
|
|
<script src="lang/valid-langs.js"></script>
|
|
<script src="app.js"></script>
|
|
</body>
|
|
</html>
|