- Add log view under repos
- Make the logs a bit nicer by adding context-sensitive icons - Fix some of the log descriptions
This commit is contained in:
parent
64c9081587
commit
782405fe65
7 changed files with 108 additions and 23 deletions
|
@ -1058,9 +1058,12 @@ def delete_webhook(namespace_name, repository_name, public_id):
|
||||||
webhook.delete_instance()
|
webhook.delete_instance()
|
||||||
return webhook
|
return webhook
|
||||||
|
|
||||||
def list_logs(user_or_organization_name):
|
def list_logs(user_or_organization_name, repository = None):
|
||||||
week_ago = datetime.today() - timedelta(7) # One week
|
week_ago = datetime.today() - timedelta(7) # One week
|
||||||
joined = LogEntry.select().join(User)
|
joined = LogEntry.select().join(User)
|
||||||
|
if repository:
|
||||||
|
joined = joined.where(LogEntry.repository == repository)
|
||||||
|
|
||||||
return joined.where(User.username == user_or_organization_name, LogEntry.datetime >= week_ago).order_by(LogEntry.datetime.desc())
|
return joined.where(User.username == user_or_organization_name, LogEntry.datetime >= week_ago).order_by(LogEntry.datetime.desc())
|
||||||
|
|
||||||
def log_action(kind_name, user_or_organization_name, performer=None, repository=None,
|
def log_action(kind_name, user_or_organization_name, performer=None, repository=None,
|
||||||
|
|
|
@ -713,6 +713,27 @@ def update_repo_api(namespace, repository):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/repository/<path:repository>/logs', methods=['GET'])
|
||||||
|
@api_login_required
|
||||||
|
@parse_repository_name
|
||||||
|
def repo_logs_api(namespace, repository):
|
||||||
|
permission = AdministerRepositoryPermission(namespace, repository)
|
||||||
|
if permission.can():
|
||||||
|
print namespace
|
||||||
|
print repository
|
||||||
|
repo = model.get_repository(namespace, repository)
|
||||||
|
print repo
|
||||||
|
if not repo:
|
||||||
|
abort(404)
|
||||||
|
|
||||||
|
logs = model.list_logs(namespace, repository = repo)
|
||||||
|
return jsonify({
|
||||||
|
'logs': [log_view(log) for log in logs]
|
||||||
|
})
|
||||||
|
|
||||||
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/repository/<path:repository>/changevisibility',
|
@app.route('/api/repository/<path:repository>/changevisibility',
|
||||||
methods=['POST'])
|
methods=['POST'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
|
@ -1201,7 +1222,7 @@ def change_team_permissions(namespace, repository, teamname):
|
||||||
new_permission['role'])
|
new_permission['role'])
|
||||||
|
|
||||||
log_action('change_repo_permission', namespace,
|
log_action('change_repo_permission', namespace,
|
||||||
{'team': team, 'repo': repository, 'role': new_permission['role']},
|
{'team': teamname, 'repo': repository, 'role': new_permission['role']},
|
||||||
repo=model.get_repository(namespace, repository))
|
repo=model.get_repository(namespace, repository))
|
||||||
|
|
||||||
resp = jsonify(role_view(perm))
|
resp = jsonify(role_view(perm))
|
||||||
|
@ -1324,7 +1345,7 @@ def change_token(namespace, repository, code):
|
||||||
new_permission['role'])
|
new_permission['role'])
|
||||||
|
|
||||||
log_action('change_repo_permission', namespace,
|
log_action('change_repo_permission', namespace,
|
||||||
{'repo': repository, 'token': token.friendly_name, 'code': code},
|
{'repo': repository, 'token': token.friendly_name, 'code': code, 'role': new_permission['role']},
|
||||||
repo = model.get_repository(namespace, repository))
|
repo = model.get_repository(namespace, repository))
|
||||||
|
|
||||||
resp = jsonify(token_view(token))
|
resp = jsonify(token_view(token))
|
||||||
|
@ -1688,10 +1709,7 @@ def delete_org_robot(orgname, robot_shortname):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/organization/<orgname>/logs', methods=['GET'])
|
def log_view(log):
|
||||||
@api_login_required
|
|
||||||
def org_logs_api(orgname):
|
|
||||||
def log_view(log):
|
|
||||||
view = {
|
view = {
|
||||||
'kind': log.kind.name,
|
'kind': log.kind.name,
|
||||||
'metadata': json.loads(log.metadata_json),
|
'metadata': json.loads(log.metadata_json),
|
||||||
|
@ -1707,6 +1725,9 @@ def org_logs_api(orgname):
|
||||||
|
|
||||||
return view
|
return view
|
||||||
|
|
||||||
|
@app.route('/api/organization/<orgname>/logs', methods=['GET'])
|
||||||
|
@api_login_required
|
||||||
|
def org_logs_api(orgname):
|
||||||
permission = AdministerOrganizationPermission(orgname)
|
permission = AdministerOrganizationPermission(orgname)
|
||||||
if permission.can():
|
if permission.can():
|
||||||
logs = model.list_logs(orgname)
|
logs = model.list_logs(orgname)
|
||||||
|
@ -1715,3 +1736,4 @@ def org_logs_api(orgname):
|
||||||
})
|
})
|
||||||
|
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ html, body {
|
||||||
border-bottom: 1px dashed #aaa;
|
border-bottom: 1px dashed #aaa;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.entity-reference .prefix {
|
.entity-reference .prefix {
|
||||||
color: #aaa;
|
color: #aaa;
|
||||||
}
|
}
|
||||||
|
@ -142,6 +143,10 @@ html, body {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.logs-view-element .log i.fa {
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
.logs-view-element .log .circle {
|
.logs-view-element .log .circle {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 12px;
|
width: 12px;
|
||||||
|
|
|
@ -678,7 +678,8 @@ quayApp.directive('logsView', function () {
|
||||||
scope: {
|
scope: {
|
||||||
'organization': '=organization',
|
'organization': '=organization',
|
||||||
'user': '=user',
|
'user': '=user',
|
||||||
'visible': '=visible'
|
'visible': '=visible',
|
||||||
|
'repository': '=repository'
|
||||||
},
|
},
|
||||||
controller: function($scope, $element, $sce, Restangular) {
|
controller: function($scope, $element, $sce, Restangular) {
|
||||||
$scope.loading = true;
|
$scope.loading = true;
|
||||||
|
@ -696,17 +697,33 @@ quayApp.directive('logsView', function () {
|
||||||
'create_repo': 'Create Repository: {repo}',
|
'create_repo': 'Create Repository: {repo}',
|
||||||
'push_repo': 'Push to repository: {repo}',
|
'push_repo': 'Push to repository: {repo}',
|
||||||
'pull_repo': function(metadata) {
|
'pull_repo': function(metadata) {
|
||||||
if (metadata.token) {
|
if (metadata.token) {
|
||||||
return 'Pull repository {repo} via token {token}';
|
return 'Pull repository {repo} via token {token}';
|
||||||
} else if (metadata.username) {
|
} else if (metadata.username) {
|
||||||
return 'Pull repository {repo} by {username}';
|
return 'Pull repository {repo} by {username}';
|
||||||
} else {
|
} else {
|
||||||
return 'Public pull of repository {repo} by {_ip}';
|
return 'Public pull of repository {repo} by {_ip}';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'delete_repo': 'Delete repository: {repo}',
|
'delete_repo': 'Delete repository: {repo}',
|
||||||
'change_repo_permission': 'Change permission for user {username} in repository {repo} to {role}',
|
'change_repo_permission': function(metadata) {
|
||||||
'delete_repo_permission': 'Remove permission for user {username} from repository {repo}',
|
if (metadata.username) {
|
||||||
|
return 'Change permission for user {username} in repository {repo} to {role}';
|
||||||
|
} else if (metadata.team) {
|
||||||
|
return 'Change permission for team {team} in repository {repo} to {role}';
|
||||||
|
} else if (metadata.token) {
|
||||||
|
return 'Change permission for token {token} in repository {repo} to {role}';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'delete_repo_permission': function(metadata) {
|
||||||
|
if (metadata.username) {
|
||||||
|
return 'Remove permission for user {username} from repository {repo}';
|
||||||
|
} else if (metadata.team) {
|
||||||
|
return 'Remove permission for team {team} from repository {repo}';
|
||||||
|
} else if (metadata.token) {
|
||||||
|
return 'Remove permission for token {token} from repository {repo}';
|
||||||
|
}
|
||||||
|
},
|
||||||
'change_repo_visibility': 'Change visibility for repository {repo} to {visibility}',
|
'change_repo_visibility': 'Change visibility for repository {repo} to {visibility}',
|
||||||
'add_repo_accesstoken': 'Create access token {token} in repository {repo}',
|
'add_repo_accesstoken': 'Create access token {token} in repository {repo}',
|
||||||
'delete_repo_accesstoken': 'Delete access token {token} in repository {repo}',
|
'delete_repo_accesstoken': 'Delete access token {token} in repository {repo}',
|
||||||
|
@ -751,14 +768,20 @@ quayApp.directive('logsView', function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
var update = function() {
|
var update = function() {
|
||||||
if (!$scope.visible || (!$scope.organization && !$scope.user)) {
|
if (!$scope.visible || (!$scope.organization && !$scope.user && !$scope.repository)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.loading = true;
|
$scope.loading = true;
|
||||||
|
|
||||||
var url = $scope.organization ? getRestUrl('organization', $scope.organization.name, 'logs') :
|
var url = getRestUrl('user/logs');
|
||||||
getRestUrl('user/logs');
|
if ($scope.organization) {
|
||||||
|
url = getRestUrl('organization', $scope.organization.name, 'logs');
|
||||||
|
}
|
||||||
|
if ($scope.repository) {
|
||||||
|
url = getRestUrl('repository', $scope.repository.namespace, $scope.repository.name, 'logs');
|
||||||
|
}
|
||||||
|
|
||||||
var loadLogs = Restangular.one(url);
|
var loadLogs = Restangular.one(url);
|
||||||
loadLogs.customGET().then(function(resp) {
|
loadLogs.customGET().then(function(resp) {
|
||||||
if (!$scope.chart) {
|
if (!$scope.chart) {
|
||||||
|
@ -788,6 +811,14 @@ quayApp.directive('logsView', function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.getDescription = function(log) {
|
$scope.getDescription = function(log) {
|
||||||
|
var fieldIcons = {
|
||||||
|
'username': 'user',
|
||||||
|
'team': 'group',
|
||||||
|
'token': 'key',
|
||||||
|
'repo': 'hdd',
|
||||||
|
'robot': 'wrench'
|
||||||
|
};
|
||||||
|
|
||||||
log.metadata['_ip'] = log.ip;
|
log.metadata['_ip'] = log.ip;
|
||||||
|
|
||||||
var description = logDescriptions[log.kind] || logTitles[log.kind] || log.kind;
|
var description = logDescriptions[log.kind] || logTitles[log.kind] || log.kind;
|
||||||
|
@ -799,6 +830,12 @@ quayApp.directive('logsView', function () {
|
||||||
if (log.metadata.hasOwnProperty(key)) {
|
if (log.metadata.hasOwnProperty(key)) {
|
||||||
var markedDown = getMarkedDown(log.metadata[key].toString());
|
var markedDown = getMarkedDown(log.metadata[key].toString());
|
||||||
markedDown = markedDown.substr('<p>'.length, markedDown.length - '<p></p>'.length);
|
markedDown = markedDown.substr('<p>'.length, markedDown.length - '<p></p>'.length);
|
||||||
|
|
||||||
|
var icon = fieldIcons[key];
|
||||||
|
if (icon) {
|
||||||
|
markedDown = '<i class="fa fa-' + icon + '"></i>' + markedDown;
|
||||||
|
}
|
||||||
|
|
||||||
description = description.replace('{' + key + '}', '<code>' + markedDown + '</code>');
|
description = description.replace('{' + key + '}', '<code>' + markedDown + '</code>');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -807,6 +844,7 @@ quayApp.directive('logsView', function () {
|
||||||
|
|
||||||
$scope.$watch('organization', update);
|
$scope.$watch('organization', update);
|
||||||
$scope.$watch('user', update);
|
$scope.$watch('user', update);
|
||||||
|
$scope.$watch('repository', update);
|
||||||
$scope.$watch('visible', update);
|
$scope.$watch('visible', update);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -426,6 +426,11 @@ function RepoAdminCtrl($scope, Restangular, $routeParams, $rootScope) {
|
||||||
var name = $routeParams.name;
|
var name = $routeParams.name;
|
||||||
|
|
||||||
$scope.permissions = {'team': [], 'user': []};
|
$scope.permissions = {'team': [], 'user': []};
|
||||||
|
$scope.logsShown = 0;
|
||||||
|
|
||||||
|
$scope.loadLogs = function() {
|
||||||
|
$scope.logsShown++;
|
||||||
|
};
|
||||||
|
|
||||||
$scope.grantRole = function() {
|
$scope.grantRole = function() {
|
||||||
$('#confirmaddoutsideModal').modal('hide');
|
$('#confirmaddoutsideModal').modal('hide');
|
||||||
|
|
|
@ -1289,7 +1289,12 @@ LogUsageChart.prototype.buildData_ = function(logs) {
|
||||||
var log = logs[i];
|
var log = logs[i];
|
||||||
var title = this.titleMap_[log.kind] || log.kind;
|
var title = this.titleMap_[log.kind] || log.kind;
|
||||||
var datetime = parseDate(log.datetime);
|
var datetime = parseDate(log.datetime);
|
||||||
var formatted = (datetime.getMonth() + 1) + '/' + datetime.getDate();
|
var dateDay = datetime.getDate();
|
||||||
|
if (dateDay < 10) {
|
||||||
|
dateDay = '0' + dateDay;
|
||||||
|
}
|
||||||
|
|
||||||
|
var formatted = (datetime.getMonth() + 1) + '/' + dateDay;
|
||||||
var adjusted = new Date(datetime.getFullYear(), datetime.getMonth(), datetime.getDate());
|
var adjusted = new Date(datetime.getFullYear(), datetime.getMonth(), datetime.getDate());
|
||||||
var key = title + '_' + formatted;
|
var key = title + '_' + formatted;
|
||||||
var found = map[key];
|
var found = map[key];
|
||||||
|
@ -1374,7 +1379,8 @@ LogUsageChart.prototype.buildData_ = function(logs) {
|
||||||
* Renders the tooltip when hovering over an element in the chart.
|
* Renders the tooltip when hovering over an element in the chart.
|
||||||
*/
|
*/
|
||||||
LogUsageChart.prototype.renderTooltip_ = function(d, e) {
|
LogUsageChart.prototype.renderTooltip_ = function(d, e) {
|
||||||
var entry = this.entries_[d + '_' + e];
|
var key = d + '_' + e;
|
||||||
|
var entry = this.entries_[key];
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
entry = {'count': 0};
|
entry = {'count': 0};
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,13 +23,19 @@
|
||||||
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#webhook" ng-click="loadWebhooks()">Web Hooks</a></li>
|
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#webhook" ng-click="loadWebhooks()">Web Hooks</a></li>
|
||||||
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#publicprivate">Public/Private</a></li>
|
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#publicprivate">Public/Private</a></li>
|
||||||
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#delete">Delete</a></li>
|
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#delete">Delete</a></li>
|
||||||
|
<li><a href="javascript:void(0)" data-toggle="tab" data-target="#logs" ng-click="loadLogs()">Usage Logs</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Content -->
|
<!-- Content -->
|
||||||
<div class="col-md-10">
|
<div class="col-md-10">
|
||||||
|
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
|
<!-- Logs tab -->
|
||||||
|
<div id="logs" class="tab-pane">
|
||||||
|
<div class="logs-view" repository="repo" visible="logsShown"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Permissions tab -->
|
<!-- Permissions tab -->
|
||||||
<div id="permissions" class="tab-pane active">
|
<div id="permissions" class="tab-pane active">
|
||||||
<!-- User Access Permissions -->
|
<!-- User Access Permissions -->
|
||||||
|
|
Reference in a new issue