mirror of https://github.com/status-im/consul.git
ui: more acl integration, condensing top bar
This commit is contained in:
parent
a133d9ecba
commit
e970ea8ddf
148
ui/index.html
148
ui/index.html
|
@ -77,21 +77,25 @@
|
|||
<script type="text/x-handlebars" id="actionbar">
|
||||
<div class="row">
|
||||
<div class="action-bar">
|
||||
<div class="col-md-5">
|
||||
<div {{ bind-attr class="searchBar:col-md-12:col-md-5" }} >
|
||||
<div class="form-group">
|
||||
{{ input type="text" value=filter class="form-control form-control-mini" placeholder="Filter by name"}}
|
||||
{{ input type="text" value=filter class="form-control form-control-mini" placeholder=filterText}}
|
||||
</div>
|
||||
</div>
|
||||
{{#if statuses}}
|
||||
<div class="col-md-5">
|
||||
<div class="form-group">
|
||||
{{view Ember.Select content=statuses value=status class="form-control form-control-mini"}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if hasExpanded }}
|
||||
<div class="col-md-2 hidden-xs hidden-sm">
|
||||
<div class="form-group">
|
||||
<button {{ bind-attr class=":btn :btn-mini :pull-right condensed:btn-default:btn-primary" }} {{action toggleCondensed }}>Expand</button>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
@ -105,11 +109,11 @@
|
|||
</div>
|
||||
|
||||
<div class="col-md-2 col-sm-3 col-xs-8 col-sm-offset-0 col-xs-offset-1">
|
||||
{{#link-to 'services' class='btn btn-default col-xs-12'}}Services{{/link-to}}
|
||||
{{#link-to 'services' (query-params status=checkStatus) tagName="div" href=false }}<a {{bind-attr class=":col-xs-12 :btn hasFailingChecks:btn-warning:btn-success"}}>Services</a>{{/link-to}}
|
||||
</div>
|
||||
|
||||
<div class="col-md-2 col-sm-3 col-xs-8 col-sm-offset-0 col-xs-offset-1">
|
||||
{{#link-to 'nodes' class='btn btn-default col-xs-12'}}Nodes{{/link-to}}
|
||||
{{#link-to 'nodes' (query-params status=checkStatus) tagName="div" href=false }}<a {{bind-attr class=":col-xs-12 :btn hasFailingChecks:btn-warning:btn-success"}}>Nodes</a>{{/link-to}}
|
||||
</div>
|
||||
|
||||
<div class="col-md-2 col-sm-3 col-xs-8 col-sm-offset-0 col-xs-offset-1">
|
||||
|
@ -120,12 +124,8 @@
|
|||
{{#link-to 'acls' class='btn btn-default col-xs-12'}}ACL{{/link-to}}
|
||||
</div>
|
||||
|
||||
<div class="col-md-1 col-md-offset-1 col-sm-3 col-sm-offset-1 col-xs-10 col-xs-offset-1">
|
||||
{{#link-to 'services' (query-params status=checkStatus) tagName="div" href=false }}<a {{bind-attr class=":col-xs-12 :btn hasFailingChecks:btn-warning:btn-success"}}>{{ checkMessage }}</a>{{/link-to}}
|
||||
</div>
|
||||
|
||||
<div class="col-md-1 col-sm-2 col-xs-6 col-sm-offset-0 col-xs-offset-1">
|
||||
<a {{bind-attr class=":col-xs-6 :btn isDropDownVisible:btn-primary:btn-default"}} {{action "toggle"}}> <span class="elip-overflow">{{model}} <span class="caret"></span></span> </a>
|
||||
<div class="col-md-2 col-md-offset-1 col-sm-2 col-xs-6 col-sm-offset-0 col-xs-offset-1">
|
||||
<a {{bind-attr class=":col-xs-12 :btn isDropDownVisible:btn-primary:btn-default"}} {{action "toggle"}}> <span class="elip-overflow">{{model}} <span class="caret"></span></span> </a>
|
||||
|
||||
{{#if isDropdownVisible}}
|
||||
<ul class="dropdown-menu col-xs-8" style="display:block;">
|
||||
|
@ -559,44 +559,16 @@
|
|||
{{view App.ActionBarView }}
|
||||
|
||||
{{#if filteredContent}}
|
||||
|
||||
{{#if condensed }}
|
||||
{{#collection Ember.ListView contentBinding="filteredContent" height=800 rowHeight=44 }}
|
||||
{{#link-to 'acl.show' Node tagName="div" href=false class="list-group-item list-condensed-link" }}
|
||||
<div {{bind-attr class=":list-bar-horizontal"}}></div>
|
||||
<div class="name">
|
||||
foo
|
||||
<small class="pull-right">
|
||||
bar
|
||||
</small>
|
||||
</div>
|
||||
{{/link-to}}
|
||||
{{/collection}}
|
||||
|
||||
{{else}}
|
||||
|
||||
{{#collection Ember.ListView contentBinding="filteredContent" height=800 rowHeight=120 }}
|
||||
{{#link-to 'nodes.show' Node tagName="div" href=false class="list-group-item list-link" }}
|
||||
<div {{bind-attr class="hasFailingChecks:bg-orange:bg-green :list-bar"}}></div>
|
||||
<h4 class="list-group-item-heading">
|
||||
{{Node}}
|
||||
<small>{{Address}}</small>
|
||||
<div class="heading-helper">
|
||||
<a class="subtle" href="#">{{checkMessage}}</a>
|
||||
</div>
|
||||
</h4>
|
||||
<ul class="list-inline">
|
||||
{{#each service in services}}
|
||||
<li class="bold">{{service.Service}}</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{/link-to}}
|
||||
{{/collection}}
|
||||
|
||||
{{/if}}
|
||||
|
||||
{{#collection Ember.ListView contentBinding="filteredContent" height=800 rowHeight=44 }}
|
||||
{{#link-to 'acls.show' ID tagName="div" href=false class="list-group-item list-condensed-link" }}
|
||||
<div class="bg-light-gray list-bar-horizontal"></div>
|
||||
<div class="name">
|
||||
{{ aclName Name ID }}
|
||||
</div>
|
||||
{{/link-to}}
|
||||
{{/collection}}
|
||||
{{else}}
|
||||
<p class="light">There are no nodes to show.</p>
|
||||
<p class="light">There are no ACLs to show.</p>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
|
@ -613,81 +585,25 @@
|
|||
|
||||
<script type="text/x-handlebars" id="acl">
|
||||
<div class="col-xs-12 col-sm-12 visible-xs visible-sm">
|
||||
{{#link-to "nodes" class="btn btn-default btn-block" }}Back to all nodes{{/link-to}}
|
||||
{{#link-to "acls" class="btn btn-default btn-block" }}Back to all ACLs{{/link-to}}
|
||||
<hr>
|
||||
</div>
|
||||
|
||||
<h3 class="no-margin">{{ model.Node }} <small> {{ model.Address }}</small></h3>
|
||||
{{errorMessage}}
|
||||
|
||||
<h3 class="no-margin">{{aclName model.Name model.ID }}</h3>
|
||||
<hr>
|
||||
|
||||
<h5>Services</h5>
|
||||
|
||||
{{#each service in model.Services }}
|
||||
{{#link-to 'services.show' service.Service tagName="div" href=false class="list-group-item list-condensed-link double-line" }}
|
||||
<div class="list-bar-horizontal bg-light-gray"></div>
|
||||
<div class="name">
|
||||
{{service.Service}}
|
||||
<small class="pull-right">
|
||||
:{{service.Port}}
|
||||
</small>
|
||||
</div>
|
||||
<ul class="list-inline sub">
|
||||
{{#each tag in service.Tags}}
|
||||
<li>{{tag}}</li>
|
||||
{{/each}}
|
||||
{{serviceTagMessage service.Tags}}
|
||||
</ul>
|
||||
{{/link-to}}
|
||||
|
||||
{{/each}}
|
||||
|
||||
<h5>Checks</h5>
|
||||
|
||||
{{#each check in model.Checks }}
|
||||
|
||||
<div class="panel">
|
||||
{{ panelBar check.Status }}
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">
|
||||
{{check.Name}}
|
||||
<small>{{check.CheckID}}</small>
|
||||
<span class="panel-note">{{check.Status}}</span>
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div class="panel-body">
|
||||
<h5>Notes</h5>
|
||||
<p>{{ check.Notes }}</p>
|
||||
<h5>Output</h5>
|
||||
<pre>{{check.Output}}</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{/each}}
|
||||
|
||||
<h5>Lock Sessions</h5>
|
||||
|
||||
{{#if sessions }}
|
||||
{{errorMessage}}
|
||||
|
||||
{{#each session in sessions }}
|
||||
<div class="list-group-item list-condensed double-line">
|
||||
<div class="bg-light-gray list-bar-horizontal"></div>
|
||||
<div class="name">
|
||||
{{ sessionName session }}
|
||||
<button {{ action "invalidateSession" session.ID }} {{ bind-attr class=":btn :btn-danger :pull-right :btn-list isLoading:btn-warning" }}>Invalidate</button>
|
||||
</div>
|
||||
<ul class="list-inline sub">
|
||||
{{#each check in session.Checks}}
|
||||
<li class="bold">{{check}}</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</div>
|
||||
{{/each}}
|
||||
|
||||
{{else}}
|
||||
<p class="light small">No sessions</p>
|
||||
<button {{ action "clone" }} {{ bind-attr class=":btn :btn-default" }}>Clone</button>
|
||||
<button {{ action "set" }} {{ bind-attr class=":btn :btn-default" }}>Use Token</button>
|
||||
{{# if model.isNotAnon }}
|
||||
<button {{ action "delete"}} {{ bind-attr class=":btn isLoading:btn-warning:btn-danger" }}>Delete</button>
|
||||
{{/if}}
|
||||
<h5>Type</h5>
|
||||
<p>{{Type}}</p>
|
||||
|
||||
<h5>Rules</h5>
|
||||
<pre>{{formatRules model.Rules}}</pre>
|
||||
</script>
|
||||
|
||||
<script type="text/x-handlebars" id="index">
|
||||
|
|
|
@ -241,6 +241,8 @@ ItemBaseController = Ember.ArrayController.extend({
|
|||
queryParams: ["filter", "status", "condensed"],
|
||||
dc: Ember.computed.alias("controllers.dc"),
|
||||
condensed: true,
|
||||
hasExpanded: true,
|
||||
filterText: "Filter by name",
|
||||
filter: "", // default
|
||||
status: "any status", // default
|
||||
statuses: ["any status", "passing", "failing"],
|
||||
|
@ -313,10 +315,16 @@ App.ServicesController = ItemBaseController.extend({
|
|||
items: Ember.computed.alias("services"),
|
||||
});
|
||||
|
||||
App.AclsIndexController = Ember.ArrayController.extend({
|
||||
App.AclsController = Ember.ArrayController.extend({
|
||||
needs: ["dc", "application"],
|
||||
queryParams: ["filter"],
|
||||
filterText: "Filter by name or ID",
|
||||
searchBar: true,
|
||||
|
||||
dc: Ember.computed.alias("controllers.dc"),
|
||||
items: Ember.computed.alias("acls"),
|
||||
|
||||
filter: "",
|
||||
|
||||
isShowingItem: function() {
|
||||
var currentPath = this.get('controllers.application.currentPath');
|
||||
|
@ -329,20 +337,95 @@ App.AclsIndexController = Ember.ArrayController.extend({
|
|||
var items = this.get('items').filter(function(item, index, enumerable){
|
||||
// First try to match on the name
|
||||
var nameMatch = item.get('Name').toLowerCase().match(filter.toLowerCase());
|
||||
if (nameMatch.length > 0) {
|
||||
if (nameMatch !== null) {
|
||||
return nameMatch;
|
||||
// Otherwise match on the ID
|
||||
} else {
|
||||
return item.get('ID').toLowerCase().match(filter.toLowerCase());
|
||||
}
|
||||
});
|
||||
|
||||
return items;
|
||||
}.property('filter', 'items.@each'),
|
||||
});
|
||||
|
||||
|
||||
App.AclsShowController = Ember.ArrayController.extend({
|
||||
needs: ["dc"],
|
||||
App.AclsShowController = Ember.ObjectController.extend({
|
||||
needs: ["dc", "acls"],
|
||||
dc: Ember.computed.alias("controllers.dc"),
|
||||
isLoading: false,
|
||||
|
||||
actions: {
|
||||
set: function() {
|
||||
this.set('isLoading', true);
|
||||
var controller = this;
|
||||
var acl = controller.get('model');
|
||||
var dc = controller.get('dc').get('datacenter');
|
||||
|
||||
if (window.confirm("Are you sure you want to use this token for your session?")) {
|
||||
// Set
|
||||
var token = App.set('settings.token', acl.ID);
|
||||
controller.transitionToRoute('services');
|
||||
this.set('isLoading', false);
|
||||
}
|
||||
},
|
||||
|
||||
clone: function() {
|
||||
this.set('isLoading', true);
|
||||
var controller = this;
|
||||
var acl = controller.get('model');
|
||||
var dc = controller.get('dc').get('datacenter');
|
||||
var token = App.get('settings.token');
|
||||
|
||||
// Set
|
||||
controller.transitionToRoute('services');
|
||||
|
||||
Ember.$.ajax({
|
||||
url: formatUrl('/v1/acl/clone/'+ acl.ID, dc, token),
|
||||
type: 'PUT'
|
||||
}).then(function(response) {
|
||||
controller.transitionToRoute('acls.show', response.ID);
|
||||
controller.set('isLoading', false);
|
||||
}).fail(function(response) {
|
||||
// Render the error message on the form if the request failed
|
||||
controller.set('errorMessage', 'Received error while processing: ' + response.statusText);
|
||||
controller.set('isLoading', false);
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
delete: function() {
|
||||
this.set('isLoading', true);
|
||||
var controller = this;
|
||||
var acl = controller.get('model');
|
||||
var dc = controller.get('dc').get('datacenter');
|
||||
var token = App.get('settings.token');
|
||||
|
||||
if (window.confirm("Are you sure you want to delete this token?")) {
|
||||
Ember.$.ajax({
|
||||
url: formatUrl('/v1/acl/destroy/'+ acl.ID, dc, token),
|
||||
type: 'PUT'
|
||||
}).then(function(response) {
|
||||
Ember.$.getJSON(formatUrl('/v1/acl/list', dc, token)).then(function(data) {
|
||||
objs = [];
|
||||
data.map(function(obj){
|
||||
if (obj.ID === "anonymous") {
|
||||
objs.unshift(App.Acl.create(obj));
|
||||
} else {
|
||||
objs.push(App.Acl.create(obj));
|
||||
}
|
||||
});
|
||||
controller.get('controllers.acls').set('acls', objs);
|
||||
}).then(function() {
|
||||
controller.transitionToRoute('acls');
|
||||
controller.set('isLoading', false);
|
||||
});
|
||||
}).fail(function(response) {
|
||||
// Render the error message on the form if the request failed
|
||||
controller.set('errorMessage', 'Received error while processing: ' + response.statusText);
|
||||
controller.set('isLoading', false);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -28,6 +28,24 @@ Ember.Handlebars.helper('sessionName', function(session) {
|
|||
}
|
||||
});
|
||||
|
||||
Ember.Handlebars.helper('aclName', function(name, id) {
|
||||
if (name === "") {
|
||||
return id;
|
||||
} else {
|
||||
return new Handlebars.SafeString(name + ' <small class="pull-right">' + id + '</small>');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Ember.Handlebars.helper('formatRules', function(rules) {
|
||||
if (rules === "") {
|
||||
return "No rules defined";
|
||||
} else {
|
||||
return rules;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// We need to do this because of our global namespace properties. The
|
||||
// service.Tags
|
||||
Ember.Handlebars.helper('serviceTagMessage', function(tags) {
|
||||
|
|
|
@ -244,4 +244,37 @@ App.Key = Ember.Object.extend(Ember.Validations.Mixin, {
|
|||
// An ACL
|
||||
//
|
||||
App.Acl = Ember.Object.extend({
|
||||
isNotAnon: function() {
|
||||
if (this.get('ID') === "anonymous"){
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}.property('ID')
|
||||
});
|
||||
|
||||
// Wrap localstorage with an ember object
|
||||
App.Settings = Ember.Object.extend({
|
||||
unknownProperty: function(key) {
|
||||
return localStorage[key];
|
||||
},
|
||||
|
||||
setUnknownProperty: function(key, value) {
|
||||
if(Ember.isNone(value)) {
|
||||
delete localStorage[key];
|
||||
} else {
|
||||
localStorage[key] = value;
|
||||
}
|
||||
this.notifyPropertyChange(key);
|
||||
return value;
|
||||
},
|
||||
|
||||
clear: function() {
|
||||
this.beginPropertyChanges();
|
||||
for (var i=0, l=localStorage.length; i<l; i++){
|
||||
this.set(localStorage.key(i));
|
||||
}
|
||||
localStorage.clear();
|
||||
this.endPropertyChanges();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -11,32 +11,6 @@ Ember.Application.initializer({
|
|||
}
|
||||
});
|
||||
|
||||
// Wrap localstorage with an ember object
|
||||
App.Settings = Ember.Object.extend({
|
||||
unknownProperty: function(key) {
|
||||
return localStorage[key];
|
||||
},
|
||||
|
||||
setUnknownProperty: function(key, value) {
|
||||
if(Ember.isNone(value)) {
|
||||
delete localStorage[key];
|
||||
} else {
|
||||
localStorage[key] = value;
|
||||
}
|
||||
this.notifyPropertyChange(key);
|
||||
return value;
|
||||
},
|
||||
|
||||
clear: function() {
|
||||
this.beginPropertyChanges();
|
||||
for (var i=0, l=localStorage.length; i<l; i++){
|
||||
this.set(localStorage.key(i));
|
||||
}
|
||||
localStorage.clear();
|
||||
this.endPropertyChanges();
|
||||
}
|
||||
});
|
||||
|
||||
App.Router.map(function() {
|
||||
// Our parent datacenter resource sets the namespace
|
||||
// for the entire application
|
||||
|
@ -61,7 +35,7 @@ App.Router.map(function() {
|
|||
});
|
||||
// ACLs
|
||||
this.resource("acls", { path: "/acls" }, function(){
|
||||
this.route("show", { path: "/:name" });
|
||||
this.route("show", { path: "/:id" });
|
||||
});
|
||||
|
||||
// Shows a page explaining that ACLs haven't been set-up
|
||||
|
|
|
@ -309,7 +309,11 @@ App.AclsRoute = App.BaseRoute.extend({
|
|||
return Ember.$.getJSON(formatUrl('/v1/acl/list', dc, token)).then(function(data) {
|
||||
objs = [];
|
||||
data.map(function(obj){
|
||||
objs.push(App.Acl.create(obj));
|
||||
if (obj.ID === "anonymous") {
|
||||
objs.unshift(App.Acl.create(obj))
|
||||
} else {
|
||||
objs.push(App.Acl.create(obj));
|
||||
}
|
||||
});
|
||||
return objs;
|
||||
});
|
||||
|
@ -334,6 +338,24 @@ App.AclsRoute = App.BaseRoute.extend({
|
|||
}
|
||||
});
|
||||
|
||||
App.AclsShowRoute = App.BaseRoute.extend({
|
||||
model: function(params) {
|
||||
var dc = this.modelFor('dc').dc;
|
||||
var token = App.get('settings.token');
|
||||
// Return a promise hash of the node and nodes
|
||||
return Ember.RSVP.hash({
|
||||
dc: dc,
|
||||
acl: Ember.$.getJSON(formatUrl('/v1/acl/info/'+ params.id, dc, token)).then(function(data) {
|
||||
return App.Acl.create(data[0]);
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
setupController: function(controller, models) {
|
||||
controller.set('content', models.acl);
|
||||
}
|
||||
});
|
||||
|
||||
// Adds any global parameters we need to set to a url/path
|
||||
function formatUrl(url, dc, token) {
|
||||
if (url.indexOf("?") > 0) {
|
||||
|
|
|
@ -29,17 +29,6 @@
|
|||
box-shadow: none;
|
||||
}
|
||||
|
||||
&.btn-primary, &.active {
|
||||
color: $purple-dark;
|
||||
background-color: transparent;
|
||||
border: 2px solid $purple;
|
||||
|
||||
&:hover {
|
||||
background-color: $light-purple;
|
||||
color: darken($purple, 10%);
|
||||
}
|
||||
}
|
||||
|
||||
&.btn-warning {
|
||||
color: $orange-faded;
|
||||
background-color: transparent;
|
||||
|
@ -61,7 +50,6 @@
|
|||
background-color: lighten($green-faded, 24%);
|
||||
color: darken($green-dark, 10%);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&.btn-danger {
|
||||
|
@ -73,7 +61,18 @@
|
|||
background-color: lighten($red, 38%);
|
||||
color: darken($red, 10%);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
&.active {
|
||||
color: $purple-dark;
|
||||
background-color: transparent;
|
||||
border: 2px solid $purple;
|
||||
|
||||
&:hover {
|
||||
background-color: $light-purple;
|
||||
color: darken($purple, 10%);
|
||||
}
|
||||
}
|
||||
|
||||
&.btn-mini {
|
||||
|
@ -89,3 +88,16 @@
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
.active {
|
||||
.btn {
|
||||
color: $purple-dark;
|
||||
background-color: transparent;
|
||||
border: 2px solid $purple;
|
||||
|
||||
&:hover {
|
||||
background-color: $light-purple;
|
||||
color: darken($purple, 10%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue