keys ui WIP

This commit is contained in:
Joseph Schorr 2016-04-01 13:55:29 -04:00 committed by Jimmy Zelinskie
parent dc593c0197
commit 11ff3e9b59
25 changed files with 1154 additions and 74 deletions

View file

@ -0,0 +1,51 @@
/**
* An element which displays a datetime picker.
*/
angular.module('quay').directive('datetimePicker', function () {
var directiveDefinitionObject = {
priority: 0,
templateUrl: '/static/directives/datetime-picker.html',
replace: false,
transclude: true,
restrict: 'C',
scope: {
'datetime': '=datetime',
},
controller: function($scope, $element) {
$scope.entered_datetime = null;
$(function() {
$element.find('input').datetimepicker({
'format': 'LLL',
'sideBySide': true,
'showClear': true
});
$element.find('input').on("dp.change", function (e) {
$scope.datetime = e.date ? e.date.unix() : null;
});
});
$scope.$watch('entered_datetime', function(value) {
if (!value) {
if ($scope.datetime) {
$scope.datetime = null;
}
return;
}
$scope.datetime = (new Date(value)).getTime()/1000;
});
$scope.$watch('datetime', function(value) {
if (!value) {
$scope.entered_datetime = null;
return;
}
$scope.entered_datetime = moment.unix(value).format('LLL');
});
}
};
return directiveDefinitionObject;
});

View file

@ -37,6 +37,14 @@ angular.module('quay').directive('logsView', function () {
return '';
};
var getServiceKeyTitle = function(metadata) {
if (metadata.name) {
return metadata.name;
}
return metadata.kind.substr(0, 12);
};
var logDescriptions = {
'account_change_plan': 'Change plan',
'account_change_cc': 'Update credit card',
@ -195,6 +203,20 @@ angular.module('quay').directive('logsView', function () {
'regenerate_robot_token': 'Regenerated token for robot {robot}',
'service_key_create': function(metadata) {
if (metadata.preshared) {
return 'Manual creation of preshared service key {kid} for service {service}';
} else {
return 'Creation of service key {kid} for service {service} by {user_agent}';
}
},
'service_key_approve': 'Approval of service key {kid}',
'service_key_modify': 'Modification of service key {kid}',
'service_key_delete': 'Deletion of service key {kid}',
'service_key_extend': 'Change of expiration of service key {kid} from {old_expiration_date} to {expiration_date}',
'service_key_rotate': 'Automatic rotation of service key {kid} by {user_agent}',
// Note: These are deprecated.
'add_repo_webhook': 'Add webhook in repository {repo}',
'delete_repo_webhook': 'Delete webhook in repository {repo}'
@ -245,6 +267,12 @@ angular.module('quay').directive('logsView', function () {
'add_repo_notification': 'Add repository notification',
'delete_repo_notification': 'Delete repository notification',
'regenerate_robot_token': 'Regenerate Robot Token',
'service_key_create': 'Create Service Key',
'service_key_approve': 'Approve Service Key',
'service_key_modify': 'Modify Service Key',
'service_key_delete': 'Delete Service Key',
'service_key_extend': 'Extend Service Key Expiration',
'service_key_rotate': 'Automatic rotation of Service Key',
// Note: these are deprecated.
'add_repo_webhook': 'Add webhook',

View file

@ -0,0 +1,32 @@
/**
* An element which display an inline editor for writing and previewing markdown text.
*/
angular.module('quay').directive('markdownEditor', function () {
var counter = 0;
var directiveDefinitionObject = {
priority: 0,
templateUrl: '/static/directives/markdown-editor.html',
replace: false,
transclude: false,
restrict: 'C',
scope: {
'content': '=content',
},
controller: function($scope, $element, $timeout) {
$scope.id = (counter++);
$scope.previewing = false;
$timeout(function() {
var converter = Markdown.getSanitizingConverter();
var editor = new Markdown.Editor(converter, '-' + $scope.id);
editor.run();
});
$scope.togglePreview = function() {
$scope.previewing = !$scope.previewing;
};
}
};
return directiveDefinitionObject;
});

View file

@ -0,0 +1,229 @@
/**
* An element which displays a panel for managing keys for external services.
*/
angular.module('quay').directive('serviceKeysManager', function () {
var directiveDefinitionObject = {
priority: 0,
templateUrl: '/static/directives/service-keys-manager.html',
replace: false,
transclude: true,
restrict: 'C',
scope: {
'isEnabled': '=isEnabled'
},
controller: function($scope, $element, ApiService, TableService) {
$scope.options = {
'filter': null,
'predicate': 'expiration_datetime',
'reverse': false,
};
$scope.TableService = TableService;
$scope.newKey = null;
$scope.creatingKey = false;
$scope.context = {
'expirationChangeInfo': null
};
var buildOrderedKeys = function() {
if (!$scope.keys) {
return;
}
var keys = $scope.keys.map(function(key) {
var expiration_datetime = -Number.MAX_VALUE;
if (key.expiration_date) {
expiration_datetime = new Date(key.expiration_date).valueOf() * (-1);
}
return $.extend(key, {
'creation_datetime': new Date(key.creation_date).valueOf() * (-1),
'expiration_datetime': expiration_datetime,
'expanded': false
});
});
$scope.orderedKeys = TableService.buildOrderedItems(keys, $scope.options,
['name', 'kid', 'service'],
['creation_datetime', 'expiration_datetime'])
};
var loadServiceKeys = function() {
$scope.options.filter = null;
$scope.now = new Date();
$scope.keysResource = ApiService.getServiceKeysAsResource().get(function(resp) {
$scope.keys = resp['keys'];
buildOrderedKeys();
});
};
$scope.getKeyTitle = function(key) {
if (!key) { return ''; }
return key.name || key.kid.substr(0, 12);
};
$scope.toggleDetails = function(key) {
key.expanded = !key.expanded;
};
$scope.getExpirationInfo = function(key) {
if (!key.expiration_date) {
return '';
}
if (key.metadata.rotation_ttl) {
var rotate_date = moment(key.created_date).add(key.metadata.rotation_ttl, 's')
if (moment().isBefore(rotate_date)) {
return {'className': 'rotation', 'icon': 'fa-refresh', 'rotateDate': rotate_date};
}
}
expiration_date = moment(key.expiration_date);
if (moment().isAfter(expiration_date)) {
return {'className': 'expired', 'icon': 'fa-warning'};
}
if (moment().add(1, 'week').isAfter(expiration_date)) {
return {'className': 'critical', 'icon': 'fa-warning'};
}
if (moment().add(1, 'month').isAfter(expiration_date)) {
return {'className': 'warning', 'icon': 'fa-warning'};
}
return {'className': 'info', 'icon': 'fa-info-circle'};
};
$scope.showChangeName = function(key) {
bootbox.prompt('Enter a friendly name for key ' + $scope.getKeyTitle(key), function(value) {
if (value) {
var data = {
'name': value
};
var params = {
'kid': key.kid
};
ApiService.updateServiceKey(data, params).then(function(resp) {
loadServiceKeys();
}, ApiService.errorDisplay('Could not update service key'));
}
});
};
$scope.showChangeExpiration = function(key) {
$scope.context.expirationChangeInfo = {
'key': key,
'expiration_date': key.expiration_date ? (new Date(key.expiration_date).getTime() / 1000) : null
};
};
$scope.changeKeyExpiration = function(changeInfo, callback) {
var errorHandler = ApiService.errorDisplay('Could not change expiration on service key', callback);
var data = {
'expiration': changeInfo.expiration_date
};
var params = {
'kid': changeInfo.key.kid
};
ApiService.updateServiceKey(data, params).then(function(resp) {
loadServiceKeys();
callback(true);
}, errorHandler);
};
$scope.createServiceKey = function() {
$scope.creatingKey = true;
ApiService.createServiceKey($scope.newKey).then(function(resp) {
$scope.creatingKey = false;
$('#createKeyModal').modal('hide');
$scope.createdKey = resp;
$('#createdKeyModal').modal('show');
loadServiceKeys();
}, ApiService.errorDisplay('Could not create service key'));
};
$scope.showApproveKey = function(key) {
$scope.approvalKeyInfo = {
'key': key,
'notes': ''
};
};
$scope.approveKey = function(approvalKeyInfo, callback) {
var errorHandler = ApiService.errorDisplay('Could not approve service key', callback);
var data = {
'notes': approvalKeyInfo.notes
};
var params = {
'kid': approvalKeyInfo.key.kid
};
ApiService.approveServiceKey(data, params).then(function(resp) {
loadServiceKeys();
callback(true);
}, errorHandler);
};
$scope.showCreateKey = function() {
$scope.newKey = {
'expiration': null
};
$('#createKeyModal').modal('show');
};
$scope.showDeleteKey = function(key) {
$scope.deleteKeyInfo = {
'key': key
};
};
$scope.deleteKey = function(deleteKeyInfo, callback) {
var errorHandler = ApiService.errorDisplay('Could not delete service key', callback);
var params = {
'kid': deleteKeyInfo.key.kid
};
ApiService.deleteServiceKey(null, params).then(function(resp) {
loadServiceKeys();
callback(true);
}, errorHandler);
};
$scope.isDownloadSupported = function() {
var isSafari = /^((?!chrome).)*safari/i.test(navigator.userAgent);
if (isSafari) {
// Doesn't work properly in Safari, sadly.
return false;
}
try { return !!new Blob(); } catch(e) {}
return false;
};
$scope.downloadPrivateKey = function(key) {
var blob = new Blob([key.private_key]);
saveAs(blob, $scope.getKeyTitle(key) + '.pem');
};
$scope.$watch('options.filter', buildOrderedKeys);
$scope.$watch('options.predicate', buildOrderedKeys);
$scope.$watch('options.reverse', buildOrderedKeys);
$scope.$watch('isEnabled', function(value) {
if (value) {
loadServiceKeys();
}
});
}
};
return directiveDefinitionObject;
});

View file

@ -31,6 +31,7 @@
$scope.csrf_token = encodeURIComponent(window.__token);
$scope.dashboardActive = false;
$scope.currentConfig = null;
$scope.serviceKeysActive = false;
$scope.setDashboardActive = function(active) {
$scope.dashboardActive = active;
@ -46,6 +47,10 @@
$('#createUserModal').modal('show');
};
$scope.loadServiceKeys = function() {
$scope.serviceKeysActive = true;
};
$scope.viewSystemLogs = function(service) {
if ($scope.pollChannel) {
$scope.pollChannel.stop();

View file

@ -25,6 +25,34 @@ angular.module('quay').factory('StringBuilderService', ['$sce', 'UtilService', f
'client_id': 'chain'
};
var filters = {
'obj': function(value) {
if (!value) { return []; }
return Object.getOwnPropertyNames(value);
},
'updated_tags': function(value) {
if (!value) { return []; }
return Object.getOwnPropertyNames(value);
},
'kid': function(kid, metadata) {
if (metadata.name) {
return metadata.name;
}
return metadata.kid.substr(0, 12);
},
'expiration_date': function(value) {
return moment.unix(value).format('LLL');
},
'old_expiration_date': function(value) {
return moment.unix(value).format('LLL');
}
};
stringBuilderService.buildUrl = function(value_or_func, metadata) {
var url = value_or_func;
if (typeof url != 'string') {
@ -105,18 +133,6 @@ angular.module('quay').factory('StringBuilderService', ['$sce', 'UtilService', f
}
stringBuilderService.buildString = function(value_or_func, metadata, opt_codetag) {
var filters = {
'obj': function(value) {
if (!value) { return []; }
return Object.getOwnPropertyNames(value);
},
'updated_tags': function(value) {
if (!value) { return []; }
return Object.getOwnPropertyNames(value);
}
};
var description = value_or_func;
if (typeof description != 'string') {
description = description(metadata);
@ -126,7 +142,7 @@ angular.module('quay').factory('StringBuilderService', ['$sce', 'UtilService', f
if (metadata.hasOwnProperty(key)) {
var value = metadata[key] != null ? metadata[key] : '(Unknown)';
if (filters[key]) {
value = filters[key](value);
value = filters[key](value, metadata);
}
description = stringBuilderService.replaceField(description, '', key, value, opt_codetag);