<!DOCTYPE html> <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="favicon.png"> <title>Syncthing | {{thisNodeName()}}</title> <link href="bootstrap/css/bootstrap.min.css" rel="stylesheet"> <style type="text/css"> body { padding-bottom: 70px; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; } ul+h5 { margin-top: 1.5em; } .text-monospace { font-family: Menlo, Monaco, Consolas, "Courier New", monospace; } .table-condensed>thead>tr>th, .table-condensed>tbody>tr>th, .table-condensed>tfoot>tr>th, .table-condensed>thead>tr>td, .table-condensed>tbody>tr>td, .table-condensed>tfoot>tr>td { border-top: none; } .logo { margin: 0; padding: 0; top: -5px; position: relative; } .list-no-bullet { list-style-type: none } .li-column { display: inline-block; min-width: 7em; margin-right: 1em; background-color: rgb(236, 240, 241); border-radius: 3px; padding: 1px 4px; margin: 2px 2px; } .li-column span.data { margin-left: 0.5em; min-width: 10em; text-align: right; display: inline-block; } .ng-cloak { display: none !important; } .table th { white-space:nowrap; font-weight: 400; } .table td { padding-left: 20px !important; } </style> </head> <body> <!-- Top bar --> <nav class="navbar navbar-top navbar-default" role="navigation"> <div class="container"> <span class="navbar-brand"><img class="logo" src="st-logo-128.png" width="32" height="32" /> Syncthing<small class="hidden-xs"> <span class="text-muted">|</span> {{thisNodeName()}}</small></span> <ul class="nav navbar-nav navbar-right"> <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown">Edit <b class="caret"></b></a> <ul class="dropdown-menu"> <li><a href="" ng-click="addRepo()"><span class="glyphicon glyphicon-hdd"></span> Add Repository</a></li> <li><a href="" ng-click="addNode()"><span class="glyphicon glyphicon-retweet"></span> Add Node</a></li> <li class="divider"></li> <li><a href="" ng-click="editSettings()"><span class="glyphicon glyphicon-cog"></span> Settings</a></li> <li><a href="" ng-click="idNode()"><span class="glyphicon glyphicon-qrcode"></span> Show ID</a></li> <li class="divider"></li> <li><a href="" ng-click="shutdown()"><span class="glyphicon glyphicon-off"></span> Shutdown</a></li> <li><a href="" ng-click="restart()"><span class="glyphicon glyphicon-refresh"></span> Restart</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 class="panel-title">Restart Needed</h3></div> <div class="panel-body"> <p>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> Restart</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-{{repo.ID | clean}}"> <span class="glyphicon glyphicon-hdd"></span> {{repo.Directory | shortPath}} <span class="pull-right hidden-xs">{{repoStatus(repo.ID)}}</span> </a> </h3> </div> <div id="repo-{{repo.ID | clean}}" class="panel-collapse collapse"> <div class="panel-body"> <div class="table-responsive"> <table class="table table-condensed table-striped"> <tbody> <tr> <th><span class="glyphicon glyphicon-tag"></span> Repository ID</th> <td class="text-right">{{repo.ID}}</td> </tr> <tr> <th><span class="glyphicon glyphicon-folder-open"></span> Folder</th> <td class="text-right">{{repo.Directory}}</td> </tr> <tr ng-if="model[repo.ID].invalid"> <th><span class="glyphicon glyphicon-warning-sign"></span> Error</th> <td class="text-right">{{model[repo.ID].invalid}}</td> </tr> <tr> <th><span class="glyphicon glyphicon-comment"></span> Synchronization</th> <td class="text-right">{{repoStatus(repo.ID)}}</td> </tr> <tr> <th><span class="glyphicon glyphicon-globe"></span> Global Repository</th> <td class="text-right">{{model[repo.ID].globalFiles | alwaysNumber}} files, {{model[repo.ID].globalBytes | binary}}B</td> </tr> <tr> <th><span class="glyphicon glyphicon-home"></span> Local Repository</th> <td class="text-right">{{model[repo.ID].localFiles | alwaysNumber}} files, {{model[repo.ID].localBytes | binary}}B</td> </tr> <tr> <th><span class="glyphicon glyphicon-cloud-download"></span> Out of Sync</th> <td class="text-right">{{model[repo.ID].needFiles | alwaysNumber}} files, {{model[repo.ID].needBytes | binary}}B</td> </tr> <tr> <th><span class="glyphicon glyphicon-lock"></span> Master Repository</th> <td class="text-right"> <span ng-if="repo.ReadOnly">Yes</span> <span ng-if="!repo.ReadOnly">No</span> </td> </tr> <tr> <th><span class="glyphicon glyphicon-unchecked"></span> Ignore Permissions</th> <td class="text-right"> <span ng-if="repo.IgnorePerms">Yes</span> <span ng-if="!repo.IgnorePerms">No</span> </td> </tr> <tr> <th><span class="glyphicon glyphicon-share-alt"></span> Shared With</th> <td class="text-right">{{sharesRepo(repo)}}</td> </tr> </tbody> </table> </div> <span class="pull-right"><a class="btn btn-sm btn-primary" href="" ng-click="editRepo(repo)"><span class="glyphicon glyphicon-pencil"></span> Edit</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-{{nodeCfg.NodeID | clean}}"><span class="glyphicon glyphicon-home"></span> {{nodeName(nodeCfg)}}</a> </h3> </div> <div id="node-{{nodeCfg.NodeID | clean}}" class="panel-collapse collapse in"> <div class="panel-body"> <div class="table-responsive"> <table class="table table-condensed table-striped"> <tbody> <tr> <th><span class="glyphicon glyphicon-th"></span> RAM Utilization</th> <td class="text-right">{{system.sys | binary}}B</td> </tr> <tr> <th><span class="glyphicon glyphicon-tasks"></span> CPU Utilization</th> <td class="text-right">{{system.cpuPercent | alwaysNumber | natural:1}}%</td> </tr> <tr> <th><span class="glyphicon glyphicon-cloud-download"></span> Download Rate</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> Upload Rate</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> Announce Server</th> <td class="text-right"> <span class="data text-success" ng-if="system.extAnnounceOK">Online</span> <span class="data text-danger" ng-if="!system.extAnnounceOK">Offline</span> </td> </tr> <tr> <th><span class="glyphicon glyphicon-tag"></span> Version</th> <td class="text-right">{{version}}</td> </tr> </tbody> </table> </div> <span class="pull-right"><a class="btn btn-sm btn-primary" href="" ng-click="editNode(nodeCfg)"><span class="glyphicon glyphicon-pencil"></span> Edit</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-{{nodeCfg.NodeID}}"> <span class="glyphicon glyphicon-retweet"></span> {{nodeName(nodeCfg)}} <span class="pull-right hidden-xs">{{nodeStatus(nodeCfg)}}</span> </a> </h3> </div> <div id="node-{{nodeCfg.NodeID}}" class="panel-collapse collapse"> <div class="panel-body"> <div class="table-responsive"> <table class="table table-condensed table-striped"> <tbody> <tr> <th><span class="glyphicon glyphicon-link"></span> Address</th> <td class="text-right">{{nodeAddr(nodeCfg)}}</td> </tr> <tr> <th><span class="glyphicon glyphicon-comment"></span> Synchronization</th> <td class="text-right">{{nodeStatus(nodeCfg)}}</td> </tr> <tr> <th><span class="glyphicon glyphicon-cloud-download"></span> Download Rate</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> Upload Rate</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> Version</th> <td class="text-right">{{nodeVer(nodeCfg)}}</td> </tr> </tbody> </table> </div> <span class="pull-right"><a class="btn btn-sm btn-primary" href="" ng-click="editNode(nodeCfg)"><span class="glyphicon glyphicon-pencil"></span> Edit</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">Notice</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> OK</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/">Support / Forum</a></li> <li><a class="navbar-link" href="https://github.com/calmh/syncthing/releases">Latest Release</a></li> <li><a class="navbar-link" href="http://discourse.syncthing.net/category/documentation">Documentation</a></li> <li><a class="navbar-link" href="https://github.com/calmh/syncthing/issues">Bugs</a></li> <li><a class="navbar-link" href="https://github.com/calmh/syncthing">Source Code</a></li> </ul> </div> </nav> <!-- Network error modal --> <div id="networkError" class="modal fade"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header alert alert-danger"> <h4 class="modal-title"> <span class="glyphicon glyphicon-exclamation-sign"></span> Connection Error </h4> </div> <div class="modal-body"> <p> Syncthing seems to be down, or there is a problem with your Internet connection. Retrying… </p> </div> </div> </div> </div> <!-- Restarting modal --> <div id="restarting" class="modal fade"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header alert alert-info"> <h4 class="modal-title"> <span class="glyphicon glyphicon-refresh"></span> Restarting </h4> </div> <div class="modal-body"> <p> Syncthing is restarting. Please hold… </p> </div> </div> </div> </div> <!-- Shutdown modal --> <div id="shutdown" class="modal fade"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header alert alert-success"> <h4 class="modal-title"> <span class="glyphicon glyphicon-off"></span> Shutdown Complete </h4> </div> <div class="modal-body"> <p> Syncthing has been shut down. </p> </div> </div> </div> </div> <!-- ID modal --> <div id="idqr" class="modal fade"> <div class="modal-dialog modal-lg"> <div class="modal-content"> <div class="modal-header"> <h4 class="modal-title"> <span class="glyphicon glyphicon-qrcode"></span> Node Identification — {{nodeName(thisNode())}} </h4> </div> <div class="modal-body"> <div class="well well-sm text-monospace text-center"> {{myID | chunkID}} </div> <img ng-if="myID" class="center-block img-thumbnail" src="qr/{{myID | chunkID}}"/> </div> <div class="modal-footer"> <button type="button" class="btn btn-default" data-dismiss="modal"><span class="glyphicon glyphicon-remove"></span> Close</button> </div> </div> </div> </div> <!-- Node editor modal --> <div id="editNode" class="modal fade"> <div class="modal-dialog modal-lg"> <div class="modal-content"> <div class="modal-header"> <h4 ng-show="!editingExisting" class="modal-title">Add Node</h4> <h4 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 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 | chunkID}}</div> <p class="help-block"> <span 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 ng-show="!editingExisting">When adding a new node, keep in mind that <em>this node</em> must be added on the other side too.</span> </span> <span ng-if="nodeEditor.nodeID.$error.required && nodeEditor.nodeID.$dirty">The node ID cannot be blank.</span> <span ng-if="nodeEditor.nodeID.$error.validNodeid && nodeEditor.nodeID.$dirty">The entered node ID does not look valid. It should be a 52 character string consisting of letters and numbers, with spaces and dashes being optional.</span> </p> </div> <div class="form-group"> <label for="name">Name</label> <input placeholder="Home Server" id="name" class="form-control" type="text" ng-model="currentNode.Name"></input> <p class="help-block">Shown instead of Node ID in the cluster status.</p> </div> <div class="form-group"> <label 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 class="help-block">Enter comma separated <span class="text-monospace">ip:port</span> addresses or <span class="text-monospace">dynamic</span> to perform automatic discovery of the address.</p> </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> Save</button> <button type="button" class="btn btn-default" data-dismiss="modal"><span class="glyphicon glyphicon-remove"></span> Close</button> <button ng-if="editingExisting && !editingSelf" type="button" class="btn btn-danger pull-left" ng-click="deleteNode()"><span class="glyphicon glyphicon-minus"></span> Delete</button> </div> </div> </div> </div> <!-- Repo editor modal --> <div id="editRepo" class="modal fade"> <div class="modal-dialog modal-lg"> <div class="modal-content"> <div class="modal-header"> <h4 ng-show="!editingExisting" class="modal-title">Add Repository</h4> <h4 ng-show="editingExisting" class="modal-title">Edit Repository</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">Repository ID</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-zaZ0-9-_.]{1,64}$/"></input> <p class="help-block"> <span ng-if="repoEditor.repoID.$valid || repoEditor.repoID.$pristine">Short identifier for the repository. Must be the same on all cluster nodes.</span> <span ng-if="repoEditor.repoID.$error.uniqueRepo">The repository ID must be unique.</span> <span ng-if="repoEditor.repoID.$error.required && repoEditor.repoID.$dirty">The repository ID cannot be blank.</span> <span 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 <code>-_.</code> characters only.</span> </p> </div> <div class="form-group" ng-class="{'has-error': repoEditor.repoPath.$invalid && repoEditor.repoPath.$dirty}"> <label for="repoPath">Repository Path</label> <input name="repoPath" placeholder="~/Documents" id="repoPath" class="form-control" type="text" ng-model="currentRepo.Directory" required></input> <p class="help-block"> <span 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 <code>~</code> can be used as a shortcut for <code>{{system.tilde}}</code>.</span> <span ng-if="repoEditor.repoPath.$error.required && repoEditor.repoPath.$dirty">The repository path cannot be blank.</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"> Repository Master </label> </div> <p class="help-block">Files are protected from changes made on other nodes, but changes made on <em>this</em> 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"> Ignore Permissions </label> </div> <p class="help-block">File permission bits are ignored when looking for changes. Use on FAT filesystems.</p> </div> <div class="form-group"> <label for="nodes">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 class="help-block">Select the nodes to share this repository with.</p> </div> </div> <div class="col-md-6"> <div class="form-group"> <div class="checkbox"> <label> <input type="checkbox" ng-model="currentRepo.simpleFileVersioning"> Simple File Versioning </label> </div> <p class="help-block">Files are moved to date stamped versions in a <code>.stversions</code> folder when replaced or deleted by syncthing.</p> </div> <div class="form-group" ng-if="currentRepo.simpleFileVersioning" ng-class="{'has-error': repoEditor.simpleKeep.$invalid && repoEditor.simpleKeep.$dirty}"> <label 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 ng-if="repoEditor.simpleKeep.$valid || repoEditor.simpleKeep.$pristine">The number of old versions to keep, per file.</span> <span ng-if="repoEditor.simpleKeep.$error.required && repoEditor.simpleKeep.$dirty">The number of versions must be a number and cannot be blank.</span> <span ng-if="repoEditor.simpleKeep.$error.min && repoEditor.simpleKeep.$dirty">You must keep at least one version.</span> </p> </div> </div> </div> </form> <div 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> Save</button> <button type="button" class="btn btn-default" data-dismiss="modal"><span class="glyphicon glyphicon-remove"></span> Close</button> <button ng-if="editingExisting" type="button" class="btn btn-danger pull-left" ng-click="deleteRepo()"><span class="glyphicon glyphicon-minus"></span> Delete</button> </div> </div> </div> </div> <!-- Settings modal --> <div id="settings" class="modal fade"> <div class="modal-dialog modal-lg"> <div class="modal-content"> <div class="modal-header"> <h4 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" ng-repeat="setting in settings"> <div ng-if="setting.type == 'text' || setting.type == 'number'"> <label for="{{setting.id}}">{{setting.descr}}</label> <input id="{{setting.id}}" class="form-control" type="{{setting.type}}" ng-model="config.workingOptions[setting.id]"></input> </div> <div class="checkbox" ng-if="setting.type == 'bool'"> <label> {{setting.descr}} <input id="{{setting.id}}" type="checkbox" ng-model="config.workingOptions[setting.id]"></input> </label> </div> </div> </div> <div class="col-md-6"> <div class="form-group" ng-repeat="setting in guiSettings"> <div ng-if="setting.type == 'text' || setting.type == 'number' || setting.type == 'password'"> <label for="{{setting.id}}">{{setting.descr}}</label> <input id="{{setting.id}}" class="form-control" type="{{setting.type}}" ng-model="config.workingGUI[setting.id]"></input> </div> <div class="checkbox" ng-if="setting.type == 'bool'"> <label> {{setting.descr}} <input id="{{setting.id}}" type="checkbox" ng-model="config.workingGUI[setting.id]"></input> </label> </div> </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> Save</button> <button type="button" class="btn btn-default" data-dismiss="modal"><span class="glyphicon glyphicon-remove"></span> Close</button> </div> </div> </div> </div> <script src="angular.min.js"></script> <script src="jquery-2.0.3.min.js"></script> <script src="bootstrap/js/bootstrap.min.js"></script> <script src="app.js"></script> </body> </html>