mirror of
https://github.com/Quiq/docker-registry-ui.git
synced 2025-09-08 16:39:03 +00:00
Major rewrite with just breaking changes
This commit is contained in:
@@ -4,19 +4,20 @@
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Docker Registry UI</title>
|
||||
<link rel="stylesheet" type="text/css" href="{{ basePath }}/static/datatables.min.css"/>
|
||||
<script type="text/javascript" src="{{ basePath }}/static/datatables.min.js"></script>
|
||||
<title>Registry UI</title>
|
||||
<link rel="stylesheet" type="text/css" href="{{ basePath }}/static/css/bootstrap-icons.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="{{ basePath }}/static/css/datatables.min.css"/>
|
||||
<script type="text/javascript" src="{{ basePath }}/static/js/datatables.min.js"></script>
|
||||
{{yield head()}}
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div style="float: left">
|
||||
<h2><a href="{{ basePath }}/" style="text-decoration: none">Docker Registry UI</a></h2>
|
||||
<h2><a href="{{ basePath }}/" style="text-decoration: none"><i class="bi-journals"></i> Registry UI</a></h2>
|
||||
</div>
|
||||
{{if eventsAllowed}}
|
||||
<div style="float: right">
|
||||
<h4><a href="{{ basePath }}/events">Event Log</a></h4>
|
||||
<h4><a href="{{ basePath }}/event-log" style="text-decoration: none"><i class="bi-calendar-week"></i> Event Log</a></h4>
|
||||
</div>
|
||||
{{end}}
|
||||
<div style="clear: both"></div>
|
||||
@@ -25,7 +26,7 @@
|
||||
|
||||
<div style="padding: 10px 0; margin-bottom: 20px">
|
||||
<div style="text-align: center; color:darkgrey">
|
||||
Docker Registry UI v{{version}} | <a href="https://quiq.com">Quiq Inc.</a>
|
||||
Registry UI v{{version}} | <a href="https://quiq.com" target="_blank">Quiq Inc.</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
10
templates/breadcrumb.html
Normal file
10
templates/breadcrumb.html
Normal file
@@ -0,0 +1,10 @@
|
||||
{{ block breadcrumb() }}
|
||||
<li><a href="{{ basePath }}/">{{ registryHost }}</a></li>
|
||||
{{if . != nil}}
|
||||
{{x := ""}}
|
||||
{{range _, p := split(., "/")}}
|
||||
{{x = x + "/" + p}}
|
||||
<li><a href="{{ basePath }}{{ x }}">{{ p }}</a></li>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{ end }}
|
121
templates/catalog.html
Normal file
121
templates/catalog.html
Normal file
@@ -0,0 +1,121 @@
|
||||
{{extends "base.html"}}
|
||||
{{import "breadcrumb.html"}}
|
||||
|
||||
{{block head()}}
|
||||
<script type="text/javascript" src="{{ basePath }}/static/js/bootstrap-confirmation.min.js"></script>
|
||||
<script type="text/javascript" src="{{ basePath }}/static/js/sorting_natural.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$('#datatable_repos').DataTable({
|
||||
"pageLength": 10,
|
||||
"stateSave": true,
|
||||
"language": {
|
||||
"emptyTable": "Catalog is being initializing..."
|
||||
}
|
||||
});
|
||||
|
||||
$('#datatable_tags').DataTable({
|
||||
"pageLength": 10,
|
||||
"order": [[ 0, 'desc' ]],
|
||||
"stateSave": true,
|
||||
columnDefs: [
|
||||
{ type: 'natural', targets: 0 }
|
||||
],
|
||||
"language": {
|
||||
"emptyTable": "No tags."
|
||||
}
|
||||
})
|
||||
function populateConfirmation() {
|
||||
$('[data-toggle=confirmation]').confirmation({
|
||||
rootSelector: '[data-toggle=confirmation]',
|
||||
container: 'body'
|
||||
});
|
||||
}
|
||||
populateConfirmation()
|
||||
$('#datatable_tags').on('draw.dt', populateConfirmation)
|
||||
});
|
||||
</script>
|
||||
{{end}}
|
||||
|
||||
{{block body()}}
|
||||
<ol class="breadcrumb">
|
||||
{{ yield breadcrumb() repoPath }}
|
||||
</ol>
|
||||
|
||||
{{if len(repos)>0 || !isCatalogReady}}
|
||||
<h4>List of Repositories</h4>
|
||||
<table id="datatable_repos" class="table table-striped table-bordered dataTables_wrapper">
|
||||
<thead bgcolor="#ddd">
|
||||
<tr>
|
||||
<th>Repository</th>
|
||||
<th width="20%">Tags</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range _, repo := repos}}
|
||||
{{ full_repo_path := repoPath != "" ? repoPath+"/"+repo : repo }}
|
||||
{{if !isset(tagCounts[full_repo_path]) || (isset(tagCounts[full_repo_path]) && tagCounts[full_repo_path] > 0)}}
|
||||
<tr>
|
||||
<td><i class="bi bi-folder2" style="margin-right: 10px"></i> <a href="{{ basePath }}/{{ full_repo_path }}">{{ repo }}</a></td>
|
||||
<td>{{ tagCounts[full_repo_path] }}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{end}} {* end repos *}
|
||||
|
||||
{{if len(tags)>0}}
|
||||
<h4>List of Tags</h4>
|
||||
<table id="datatable_tags" class="table table-striped table-bordered">
|
||||
<thead bgcolor="#ddd">
|
||||
<tr>
|
||||
<th>Tag Name</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range _, tag := tags}}
|
||||
<tr>
|
||||
<td>
|
||||
<i class="bi bi-file-text" style="margin-right: 10px"></i> <a href="{{ basePath }}/{{ repoPath }}:{{ tag }}">{{ tag }}</a>
|
||||
{{if deleteAllowed}}
|
||||
<a href="{{ basePath }}/delete-tag?repoPath={{ repoPath }}&tag={{ tag }}" data-toggle="confirmation" class="btn btn-danger btn-xs pull-right" role="button">Delete</a>
|
||||
{{end}}
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{end}} {* end tags *}
|
||||
|
||||
{{if eventsAllowed and isset(events) }}
|
||||
<h4>Latest activity</h4>
|
||||
<table id="datatable_events" class="table table-striped table-bordered">
|
||||
<thead bgcolor="#ddd">
|
||||
<tr>
|
||||
<th>Action</th>
|
||||
<th>Image</th>
|
||||
<th>IP Address</th>
|
||||
<th>User</th>
|
||||
<th>Time</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range _, e := events}}
|
||||
<tr>
|
||||
<td>{{ e.Action }}</td>
|
||||
{{if hasPrefix(e.Tag,"sha256") }}
|
||||
<td title="{{ e.Tag }}">{{ e.Repository }}@{{ e.Tag[:19] }}...</td>
|
||||
{{else}}
|
||||
<td>{{ e.Repository }}:{{ e.Tag }}</td>
|
||||
{{end}}
|
||||
<td>{{ e.IP }}</td>
|
||||
<td>{{ e.User }}</td>
|
||||
<td>{{ e.Created|pretty_time }}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{end}}
|
||||
|
||||
{{end}}
|
@@ -1,26 +1,63 @@
|
||||
{{extends "base.html"}}
|
||||
{{import "breadcrumb.html"}}
|
||||
|
||||
{{block head()}}
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$('#datatable').DataTable({
|
||||
var table = $('#datatable').DataTable({
|
||||
"pageLength": 10,
|
||||
"order": [[ 4, 'desc' ]],
|
||||
"stateSave": true,
|
||||
"stateSave": false,
|
||||
"searchCols": [
|
||||
null,
|
||||
{search: $('input:checkbox[name="sha256_chk"]').val()},
|
||||
],
|
||||
"language": {
|
||||
"emptyTable": "No events."
|
||||
}
|
||||
});
|
||||
|
||||
$.fn.dataTable.ext.search.push(function( settings, searchData, index, rowData, counter ) {
|
||||
var action = $('input:checkbox[name="action_chk"]:checked').map(function() {
|
||||
return this.value;
|
||||
}).get();
|
||||
if (action.length === 0) {
|
||||
return true;
|
||||
}
|
||||
if (action.indexOf(searchData[0]) !== -1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
$('input:checkbox[name="action_chk"]').on('change', function () {
|
||||
table.draw();
|
||||
});
|
||||
|
||||
$('input:checkbox[name="sha256_chk"]').on('change', function () {
|
||||
if ($(this).prop('checked')) {
|
||||
table.column(1).search($(this).val()).draw() ;
|
||||
} else {
|
||||
table.column(1).search('').draw() ;
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{{end}}
|
||||
|
||||
{{block body()}}
|
||||
<ol class="breadcrumb">
|
||||
{{ yield breadcrumb() }}
|
||||
<li class="active">Event Log</li>
|
||||
</ol>
|
||||
|
||||
{{if eventsAllowed}}
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" name="action_chk" value="push">
|
||||
<label class="form-check-label">Hide Pull</label>
|
||||
<label class="form-check-label" style="margin-right:10px"></label>
|
||||
<input class="form-check-input" type="checkbox" name="sha256_chk" value="!@sha256" checked>
|
||||
<label class="form-check-label">Hide sha256 entries</label>
|
||||
</div>
|
||||
<table id="datatable" class="table table-striped table-bordered">
|
||||
<thead bgcolor="#ddd">
|
||||
<tr>
|
||||
@@ -32,7 +69,7 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range e := events}}
|
||||
{{range _, e := events}}
|
||||
<tr>
|
||||
<td>{{ e.Action }}</td>
|
||||
{{if hasPrefix(e.Tag,"sha256") }}
|
||||
|
90
templates/image_info.html
Normal file
90
templates/image_info.html
Normal file
@@ -0,0 +1,90 @@
|
||||
{{extends "base.html"}}
|
||||
{{import "breadcrumb.html"}}
|
||||
{{import "json_to_table.html"}}
|
||||
|
||||
{{block head()}}
|
||||
<style>
|
||||
/* col 0 style */
|
||||
td:nth-child(1) {
|
||||
color: #838383;
|
||||
text-align: right;
|
||||
}
|
||||
/* td: long line wrap */
|
||||
td {
|
||||
word-break: break-word;
|
||||
}
|
||||
</style>
|
||||
{{end}}
|
||||
|
||||
{{block body()}}
|
||||
<ol class="breadcrumb">
|
||||
{{ yield breadcrumb() ii.ImageRefRepo }}
|
||||
<li><a href="{{ basePath }}/{{ repoPath }}">{{ ii.ImageRefTag }}</a></li>
|
||||
</ol>
|
||||
|
||||
|
||||
<h4>
|
||||
{{if ii.IsImage}}<i class="bi-file-earmark" style="font-size: 2rem;"></i> Image{{end}}
|
||||
{{if ii.IsImageIndex}}<i class="bi-files" style="font-size: 2rem;"></i> Image Index{{end}}
|
||||
</h4>
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead bgcolor="#ddd">
|
||||
<tr>
|
||||
<th colspan="2">Summary</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr>
|
||||
<td width="20%"><b>Image Reference</b></td><td>{{ registryHost }}/{{ repoPath }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Digest</b></td><td><a href="{{ basePath }}/{{ ii.ImageRefRepo }}@{{ ii.ImageRefDigest }}">{{ ii.ImageRefDigest }}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Media Type</b></td><td>{{ ii.MediaType }}</td>
|
||||
</tr>
|
||||
{{if ii.IsImageIndex}}
|
||||
<tr>
|
||||
<td><b>Sub-Images</b></td><td>{{ len(ii.Manifest["manifests"]) }} </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Platforms</b></td><td>{{ ii.Platforms }}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
{{if ii.IsImage}}
|
||||
<tr>
|
||||
<td><b>Image ID</b></td><td>{{ ii.ConfigImageID }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Image Size</b></td><td>{{ ii.ImageSize|pretty_size }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Platform</b></td><td>{{ ii.Platforms }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Created On</b></td><td>{{ ii.Created|pretty_time }}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</table>
|
||||
|
||||
<table class="table" style="margin-bottom: 0">
|
||||
<thead bgcolor="#ddd">
|
||||
<tr>
|
||||
<th>{{if ii.IsImage}}Manifest{{else}}Index Manifest{{end}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
{{ yield json_to_table() ii.Manifest }}
|
||||
|
||||
{{if ii.IsImage}}
|
||||
<br>
|
||||
<table class="table" style="margin-bottom: 0">
|
||||
<thead bgcolor="#ddd">
|
||||
<tr>
|
||||
<th>Config File</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
{{ yield json_to_table() ii.ConfigFile }}
|
||||
{{end}}
|
||||
|
||||
{{end}}
|
32
templates/json_to_table.html
Normal file
32
templates/json_to_table.html
Normal file
@@ -0,0 +1,32 @@
|
||||
{{ block json_to_table() }}
|
||||
|
||||
{{ try }}
|
||||
<table class="table table-striped table-bordered" style="margin-bottom: 0">
|
||||
{{range i, k := sort_map_keys(.) }}
|
||||
{{ v := .[k] }}
|
||||
<tr>
|
||||
<td width="15%" style="padding: 2px 8px;">{{k}}</td>
|
||||
<td style="padding: 2px 8px;">
|
||||
{{if ii.IsImage && k == "size"}}{{ pretty_size(v) }}
|
||||
{{else if ii.IsImageIndex && k == "digest"}}<a href="{{ basePath }}/{{ ii.ImageRefRepo }}@{{ v }}">{{ v }}</a>
|
||||
{{else}}{{ yield json_to_table() v }}{{end}}
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</table>
|
||||
|
||||
{{ catch err }}
|
||||
{{if err.Error() == "reflect: call of reflect.Value.MapKeys on slice Value"}}
|
||||
<table class="table table-striped table-bordered" style="margin-bottom: 0">
|
||||
{{range _, e := . }}
|
||||
<tr>
|
||||
<td style="text-align: left; color: #000; padding: 0px 0px;">{{ yield json_to_table() e }}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</table>
|
||||
{{else}}
|
||||
{{ . }}
|
||||
{{end}}
|
||||
{{end}} {* end try *}
|
||||
|
||||
{{ end }}
|
@@ -1,68 +0,0 @@
|
||||
{{extends "base.html"}}
|
||||
|
||||
{{block head()}}
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$('#namespace').on('change', function (e) {
|
||||
window.location = '{{ basePath }}/' + this.value;
|
||||
});
|
||||
namespace = window.location.pathname;
|
||||
namespace = namespace.replace("{{ basePath }}", "");
|
||||
if (namespace == '/') {
|
||||
namespace = 'library';
|
||||
} else {
|
||||
namespace = namespace.split('/')[1]
|
||||
}
|
||||
$('#namespace').val(namespace);
|
||||
|
||||
$('#datatable').DataTable({
|
||||
"pageLength": 25,
|
||||
"stateSave": true,
|
||||
"language": {
|
||||
"emptyTable": "No repositories in \"" + namespace + "\" namespace."
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{{end}}
|
||||
|
||||
{{block body()}}
|
||||
<div style="float: right">
|
||||
<select id="namespace" class="form-control input-sm" style="height: 36px">
|
||||
{{range namespace := namespaces}}
|
||||
<option value="{{ namespace }}">{{ namespace }}</option>
|
||||
{{end}}
|
||||
</select>
|
||||
</div>
|
||||
<div style="float: right">
|
||||
<ol class="breadcrumb">
|
||||
<li class="active">Namespace</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="{{ basePath }}/">{{ registryHost }}</a></li>
|
||||
{{if namespace != "library"}}
|
||||
<li><a href="{{ basePath }}/{{ namespace }}">{{ namespace }}</a></li>
|
||||
{{end}}
|
||||
</ol>
|
||||
|
||||
<table id="datatable" class="table table-striped table-bordered">
|
||||
<thead bgcolor="#ddd">
|
||||
<tr>
|
||||
<th>Repository</th>
|
||||
<th width="20%">Tags</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range repo := repos}}
|
||||
{{if !isset(tagCounts[namespace+"/"+repo]) || (isset(tagCounts[namespace+"/"+repo]) && tagCounts[namespace+"/"+repo] > 0)}}
|
||||
<tr>
|
||||
<td><a href="{{ basePath }}/{{ namespace }}/{{ repo|url }}">{{ repo }}</a></td>
|
||||
<td>{{ tagCounts[namespace+"/"+repo] }}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{end}}
|
@@ -1,140 +0,0 @@
|
||||
{{extends "base.html"}}
|
||||
|
||||
{{block head()}}
|
||||
<style>
|
||||
/* col 0 style */
|
||||
td:nth-child(1) {
|
||||
color: #838383;
|
||||
text-align: right;
|
||||
}
|
||||
/* td: long line wrap */
|
||||
td {
|
||||
word-break: break-word;
|
||||
}
|
||||
</style>
|
||||
{{end}}
|
||||
|
||||
{{block body()}}
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="{{ basePath }}/">{{ registryHost }}</a></li>
|
||||
{{if namespace != "library"}}
|
||||
<li><a href="{{ basePath }}/{{ namespace }}">{{ namespace }}</a></li>
|
||||
{{end}}
|
||||
<li><a href="{{ basePath }}/{{ namespace }}/{{ repo }}">{{ repo|url_decode }}</a></li>
|
||||
<li class="active">{{ tag }}</li>
|
||||
</ol>
|
||||
|
||||
<h4>Image Details</h4>
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead bgcolor="#ddd">
|
||||
<tr>
|
||||
<th colspan="2">Summary</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr>
|
||||
<td width="20%"><b>Image URL</b></td><td>{{ registryHost }}/{{ repoPath }}{{ isDigest ? "@" : ":" }}{{ tag }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Digest</b></td><td>sha256:{{ sha256 }}</td>
|
||||
</tr>
|
||||
{{if created}}
|
||||
<tr>
|
||||
<td><b>Created On</b></td><td>{{ created|pretty_time }}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
{{if not digestList}}
|
||||
<tr>
|
||||
<td><b>Image Size</b></td><td>{{ imageSize|pretty_size }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Layer Count</b></td><td>{{ layersCount }}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
<tr>
|
||||
<td><b>Manifest Formats</b></td>
|
||||
<td>{{if not isDigest}}Manifest v2 schema 1{{else}}<font color="#c2c2c2">Manifest v2 schema 1</font>{{end}} |
|
||||
{{if not digestList && layersV2}}Manifest v2 schema 2{{else}}<font color="#c2c2c2">Manifest v2 schema 2</font>{{end}} |
|
||||
{{if digestList}}Manifest List v2 schema 2{{else}}<font color="#c2c2c2">Manifest List v2 schema 2</font>{{end}}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
{{if digestList}}
|
||||
<h4>Sub-images <!-- Manifest List v2 schema 2: multi-arch or cache image --></h4>
|
||||
{{range index, manifest := digestList}}
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead bgcolor="#ddd">
|
||||
<tr>
|
||||
<th colspan="2">Manifest #{{ index+1 }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{{range key := manifest["ordered_keys"]}}
|
||||
<tr>
|
||||
<td width="20%">{{ key }}</td>
|
||||
{{if key == "platform" || key == "annotations"}}
|
||||
<td style="padding: 0">
|
||||
<table class="table table-bordered" style="padding: 0; width: 100%; margin-bottom: 0; min-height: 37px">
|
||||
<!-- Nested range does not work. Iterating via filter over the map. -->
|
||||
{{ manifest[key]|parse_map|raw }}
|
||||
</table>
|
||||
</td>
|
||||
{{else if key == "size"}}
|
||||
<td>{{ manifest[key]|pretty_size }}</td>
|
||||
{{else}}
|
||||
<td>{{ manifest[key]|raw }}</td>
|
||||
{{end}}
|
||||
</tr>
|
||||
{{end}}
|
||||
</table>
|
||||
{{end}}
|
||||
{{else if layersV2}}
|
||||
<h4>Blobs <!-- Manifest v2 schema 2--></h4>
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead bgcolor="#ddd">
|
||||
<tr>
|
||||
<th>Layer #</th>
|
||||
<th>Digest</th>
|
||||
<th>Size</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{{range index, layer := layersV2}}
|
||||
<tr>
|
||||
<td>{{ len(layersV2)-index }}</td>
|
||||
<td>{{ layer["digest"] }}</td>
|
||||
<td>{{ layer["size"]|pretty_size }}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</table>
|
||||
{{end}}
|
||||
|
||||
{{if not isDigest && layersV1}}
|
||||
<h4>Image History <!-- Manifest v2 schema 1--></h4>
|
||||
{{range index, layer := layersV1}}
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead bgcolor="#ddd">
|
||||
<tr>
|
||||
<th colspan="2">Layer #{{ len(layersV1)-index }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{{range key := layer["ordered_keys"]}}
|
||||
<tr>
|
||||
<td width="20%">{{ key }}</td>
|
||||
{{if key == "config" || key == "container_config"}}
|
||||
<td style="padding: 0">
|
||||
<table class="table table-bordered" style="padding: 0; width: 100%; margin-bottom: 0; min-height: 37px">
|
||||
<!-- Nested range does not work. Iterating via filter over the map. -->
|
||||
{{ layer[key]|parse_map|raw }}
|
||||
</table>
|
||||
</td>
|
||||
{{else if key == "created"}}
|
||||
<td>{{ layer[key]|pretty_time }}</td>
|
||||
{{else}}
|
||||
<td>{{ layer[key] }}</td>
|
||||
{{end}}
|
||||
</tr>
|
||||
{{end}}
|
||||
</table>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{end}}
|
@@ -1,92 +0,0 @@
|
||||
{{extends "base.html"}}
|
||||
|
||||
{{block head()}}
|
||||
<script type="text/javascript" src="{{ basePath }}/static/bootstrap-confirmation.min.js"></script>
|
||||
<script type="text/javascript" src="{{ basePath }}/static/sorting_natural.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$('#datatable').DataTable({
|
||||
"pageLength": 10,
|
||||
"order": [[ 0, 'desc' ]],
|
||||
"stateSave": true,
|
||||
columnDefs: [
|
||||
{ type: 'natural', targets: 0 }
|
||||
],
|
||||
"language": {
|
||||
"emptyTable": "No tags in this repository."
|
||||
}
|
||||
})
|
||||
function populateConfirmation() {
|
||||
$('[data-toggle=confirmation]').confirmation({
|
||||
rootSelector: '[data-toggle=confirmation]',
|
||||
container: 'body'
|
||||
});
|
||||
}
|
||||
populateConfirmation()
|
||||
$('#datatable').on('draw.dt', populateConfirmation)
|
||||
|
||||
|
||||
});
|
||||
</script>
|
||||
{{end}}
|
||||
|
||||
{{block body()}}
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="{{ basePath }}/">{{ registryHost }}</a></li>
|
||||
{{if namespace != "library"}}
|
||||
<li><a href="{{ basePath }}/{{ namespace }}">{{ namespace }}</a></li>
|
||||
{{end}}
|
||||
<li class="active">{{ repo|url_decode }}</li>
|
||||
</ol>
|
||||
|
||||
<table id="datatable" class="table table-striped table-bordered">
|
||||
<thead bgcolor="#ddd">
|
||||
<tr>
|
||||
<th>Tag Name</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range tag := tags}}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{{ basePath }}/{{ namespace }}/{{ repo }}/{{ tag }}">{{ tag }}</a>
|
||||
{{if deleteAllowed}}
|
||||
<a href="{{ basePath }}/{{ namespace }}/{{ repo }}/{{ tag }}/delete" data-toggle="confirmation" class="btn btn-danger btn-xs pull-right" role="button">Delete</a>
|
||||
{{end}}
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{{if eventsAllowed}}
|
||||
<h4>Latest events on this repo</h4>
|
||||
<table id="datatable_log" class="table table-striped table-bordered">
|
||||
<thead bgcolor="#ddd">
|
||||
<tr>
|
||||
<th>Action</th>
|
||||
<th>Image</th>
|
||||
<th>IP Address</th>
|
||||
<th>User</th>
|
||||
<th>Time</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range e := events}}
|
||||
<tr>
|
||||
<td>{{ e.Action }}</td>
|
||||
{{if hasPrefix(e.Tag,"sha256") }}
|
||||
<td title="{{ e.Tag }}">{{ e.Repository }}@{{ e.Tag[:19] }}...</td>
|
||||
{{else}}
|
||||
<td>{{ e.Repository }}:{{ e.Tag }}</td>
|
||||
{{end}}
|
||||
<td>{{ e.IP }}</td>
|
||||
<td>{{ e.User }}</td>
|
||||
<td>{{ e.Created|pretty_time }}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{end}}
|
||||
|
||||
{{end}}
|
Reference in New Issue
Block a user