Add functionality for new tag history view

This commit is contained in:
Lennart Blom 2018-12-01 00:24:58 +01:00
parent fe5e962488
commit c857bd8db6
3 changed files with 308 additions and 194 deletions

View file

@ -1,200 +1,217 @@
<!--
Copyright (C) 2016 Jones Magloire @Joxit
Copyright (C) 2016 Jones Magloire @Joxit
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<app>
<header>
<material-navbar>
<div class="logo">Docker Registry UI</div>
<menu></menu>
</material-navbar>
</header>
<main>
<catalog if="{route.routeName == 'home'}"></catalog>
<taglist if="{route.routeName == 'taglist'}"></taglist>
<change></change>
<add></add>
<remove></remove>
<material-snackbar></material-snackbar>
</main>
<footer>
<material-footer>
<a class="material-footer-logo" href="https://joxit.github.io/docker-registry-ui/">Docker Registry UI %%GULP_INJECT_VERSION%%</a>
<ul class="material-footer-link-list">
<li>
<a href="https://github.com/Joxit/docker-registry-ui">Contribute on GitHub</a>
</li>
<li>
<a href="https://github.com/Joxit/docker-registry-ui/blob/master/LICENSE">Privacy &amp; Terms</a>
</li>
</ul>
<header>
<material-navbar>
<div class="logo">Docker Registry UI</div>
<menu></menu>
</material-navbar>
</header>
<main>
<catalog if="{route.routeName == 'home'}"></catalog>
<taglist if="{route.routeName == 'taglist'}"></taglist>
<tag-history if="{route.routeName == 'taghistory'}"></tag-history>
<change></change>
<add></add>
<remove></remove>
<material-snackbar></material-snackbar>
</main>
<footer>
<material-footer>
<a class="material-footer-logo" href="https://joxit.github.io/docker-registry-ui/">Docker Registry UI
%%GULP_INJECT_VERSION%%</a>
<ul class="material-footer-link-list">
<li>
<a href="https://github.com/Joxit/docker-registry-ui">Contribute on GitHub</a>
</li>
<li>
<a href="https://github.com/Joxit/docker-registry-ui/blob/master/LICENSE">Privacy &amp; Terms</a>
</li>
</ul>
</material-footer>
</footer>
<script>
</material-footer>
</footer>
<script>
registryUI.appTag = this;
route.base('#!')
route('', function() {
route.routeName = 'home';
if (registryUI.catalog.display) {
registryUI.catalog.loadend = false;
registryUI.catalog.display();
}
registryUI.appTag.update();
});
route('/taglist/*', function(image) {
route.routeName = 'taglist';
registryUI.taglist.name = image
if (registryUI.taglist.display) {
registryUI.taglist.loadend = false;
registryUI.taglist.display();
}
registryUI.appTag.update();
});
registryUI.home = function() {
if(route.routeName == 'home') {
registryUI.catalog.display();
} else {
route('');
}
};
registryUI.snackbar = function (message, isError) {
registryUI.appTag.tags['material-snackbar'].addToast({'message': message, 'isError': isError}, 15000);
};
registryUI.errorSnackbar = function (message) {
return registryUI.snackbar(message, true);
}
registryUI.cleanName = function() {
const url = (registryUI.url() && registryUI.url().length > 0 && registryUI.url()) || window.location.host;
if (url) {
return url.startsWith('http') ? url.replace(/https?:\/\//, '') : url;
}
return '';
}
route.parser(null, function(path, filter) {
const f = filter
.replace(/\?/g, '\\?')
.replace(/\*/g, '([^?#]+?)')
.replace(/\.\./, '.*')
const re = new RegExp('^' + f + '$')
const args = path.match(re)
if (args) return args.slice(1)
});
registryUI.appTag = this;
route.base('#!');
route('', function () {
route.routeName = 'home';
if (registryUI.catalog.display) {
registryUI.catalog.loadend = false;
registryUI.catalog.display();
}
registryUI.appTag.update();
});
route('/taglist/*', function (image) {
route.routeName = 'taglist';
registryUI.taglist.name = image;
if (registryUI.taglist.display) {
registryUI.taglist.loadend = false;
registryUI.taglist.display();
}
registryUI.appTag.update();
});
route('/taghistory/image/*/tag/*', function (image, tag) {
console.log("Welcome to the tag history");
console.log("Image='" + image + "' with tag='" + tag + "'");
route.routeName = 'taghistory';
registryUI.isDigit = function(char) {
return char >= '0' && char <= '9';
}
registryUI.taghistory.image = image;
registryUI.taghistory.tag = tag;
registryUI.DockerImage = function (name, tag) {
this.name = name;
this.tag = tag;
riot.observable(this);
this.on('get-size', function() {
if (this.size !== undefined) {
return this.trigger('size', this.size);
if (registryUI.taghistory.display){
console.log("Displaying Tag History");
registryUI.taghistory.loadend = false;
registryUI.taghistory.display();
}
registryUI.appTag.update();
});
registryUI.home = function () {
if (route.routeName == 'home') {
registryUI.catalog.display;
} else {
route('');
}
};
registryUI.snackbar = function (message, isError) {
registryUI.appTag.tags['material-snackbar'].addToast({'message': message, 'isError': isError}, 15000);
};
registryUI.errorSnackbar = function (message) {
return registryUI.snackbar(message, true);
}
return this.fillInfo();
});
this.on('get-sha256', function() {
if (this.size !== undefined) {
return this.trigger('sha256', this.sha256);
registryUI.cleanName = function () {
const url = (registryUI.url() && registryUI.url().length > 0 && registryUI.url()) || window.location.host;
if (url) {
return url.startsWith('http') ? url.replace(/https?:\/\//, '') : url;
}
return '';
}
return this.fillInfo();
});
this.on('get-date', function() {
if (this.date !== undefined) {
return this.trigger('date', this.date);
route.parser(null, function (path, filter) {
const f = filter
.replace(/\?/g, '\\?')
.replace(/\*/g, '([^?#]+?)')
.replace(/\.\./, '.*')
const re = new RegExp('^' + f + '$')
const args = path.match(re)
if (args) return args.slice(1)
});
registryUI.isDigit = function (char) {
return char >= '0' && char <= '9';
}
return this.fillInfo();
});
};
registryUI.DockerImage._tagReduce = function (acc, e) {
if (acc.length > 0 && registryUI.isDigit(acc[acc.length - 1].charAt(0)) == registryUI.isDigit(e)) {
acc[acc.length - 1] += e;
} else {
acc.push(e);
}
return acc;
}
registryUI.DockerImage = function (name, tag) {
this.name = name;
this.tag = tag;
riot.observable(this);
this.on('get-size', function () {
if (this.size !== undefined) {
return this.trigger('size', this.size);
}
return this.fillInfo();
});
this.on('get-sha256', function () {
if (this.size !== undefined) {
return this.trigger('sha256', this.sha256);
}
return this.fillInfo();
});
this.on('get-date', function () {
if (this.date !== undefined) {
return this.trigger('date', this.date);
}
return this.fillInfo();
});
};
registryUI.DockerImage.compare = function(e1, e2) {
const tag1 = e1.tag.match(/./g).reduce(registryUI.DockerImage._tagReduce, []);
const tag2 = e2.tag.match(/./g).reduce(registryUI.DockerImage._tagReduce, []);
for (var i = 0; i < tag1.length && i < tag2.length; i++) {
const compare = tag1[i].localeCompare(tag2[i]);
if (registryUI.isDigit(tag1[i].charAt(0)) && registryUI.isDigit(tag2[i].charAt(0))) {
const diff = tag1[i] - tag2[i];
if (diff != 0) {
return diff;
}
} else if (compare != 0) {
return compare;
registryUI.DockerImage._tagReduce = function (acc, e) {
if (acc.length > 0 && registryUI.isDigit(acc[acc.length - 1].charAt(0)) == registryUI.isDigit(e)) {
acc[acc.length - 1] += e;
} else {
acc.push(e);
}
return acc;
}
}
return e1.tag.length - e2.tag.length;
};
registryUI.DockerImage.prototype.fillInfo = function() {
if (this._fillInfoWaiting) {
return;
}
this._fillInfoWaiting = true;
const oReq = new Http();
const self = this;
oReq.addEventListener('loadend', function () {
if (this.status == 200 || this.status == 202) {
const response = JSON.parse(this.responseText);
self.size = response.layers.reduce(function (acc, e) {
return acc + e.size;
}, 0);
self.sha256 = response.config.digest;
self.trigger('size', self.size);
self.trigger('sha256', self.sha256);
self.getBlobs(response.config.digest)
} else if (this.status == 404) {
registryUI.errorSnackbar('Manifest for ' + self.name + ':' + self.tag + ' not found');
} else {
registryUI.snackbar(this.responseText);
}
});
oReq.open('GET', registryUI.url() + '/v2/' + self.name + '/manifests/' + self.tag);
oReq.setRequestHeader('Accept', 'application/vnd.docker.distribution.manifest.v2+json');
oReq.send();
}
registryUI.DockerImage.compare = function (e1, e2) {
const tag1 = e1.tag.match(/./g).reduce(registryUI.DockerImage._tagReduce, []);
const tag2 = e2.tag.match(/./g).reduce(registryUI.DockerImage._tagReduce, []);
registryUI.DockerImage.prototype.getBlobs = function(blob) {
const oReq = new Http();
const self = this;
oReq.addEventListener('loadend', function () {
if (this.status == 200 || this.status == 202) {
const response = JSON.parse(this.responseText);
self.creationDate = new Date(response.created);
self.trigger('creation-date', self.creationDate);
} else if (this.status == 404) {
registryUI.errorSnackbar('Blobs for ' + self.name + ':' + self.tag + ' not found');
} else {
registryUI.snackbar(this.responseText);
for (var i = 0; i < tag1.length && i < tag2.length; i++) {
const compare = tag1[i].localeCompare(tag2[i]);
if (registryUI.isDigit(tag1[i].charAt(0)) && registryUI.isDigit(tag2[i].charAt(0))) {
const diff = tag1[i] - tag2[i];
if (diff != 0) {
return diff;
}
} else if (compare != 0) {
return compare;
}
}
return e1.tag.length - e2.tag.length;
};
registryUI.DockerImage.prototype.fillInfo = function () {
if (this._fillInfoWaiting) {
return;
}
this._fillInfoWaiting = true;
const oReq = new Http();
const self = this;
oReq.addEventListener('loadend', function () {
if (this.status == 200 || this.status == 202) {
const response = JSON.parse(this.responseText);
self.size = response.layers.reduce(function (acc, e) {
return acc + e.size;
}, 0);
self.sha256 = response.config.digest;
self.trigger('size', self.size);
self.trigger('sha256', self.sha256);
self.getBlobs(response.config.digest)
} else if (this.status == 404) {
registryUI.errorSnackbar('Manifest for ' + self.name + ':' + self.tag + ' not found');
} else {
registryUI.snackbar(this.responseText);
}
});
oReq.open('GET', registryUI.url() + '/v2/' + self.name + '/manifests/' + self.tag);
oReq.setRequestHeader('Accept', 'application/vnd.docker.distribution.manifest.v2+json');
oReq.send();
}
});
oReq.open('GET', registryUI.url() + '/v2/' + self.name + '/blobs/' + blob);
oReq.setRequestHeader('Accept', 'application/vnd.docker.distribution.manifest.v2+json');
oReq.send();
};
route.start(true);
</script>
registryUI.DockerImage.prototype.getBlobs = function (blob) {
const oReq = new Http();
const self = this;
oReq.addEventListener('loadend', function () {
if (this.status == 200 || this.status == 202) {
const response = JSON.parse(this.responseText);
self.creationDate = new Date(response.created);
self.trigger('creation-date', self.creationDate);
} else if (this.status == 404) {
registryUI.errorSnackbar('Blobs for ' + self.name + ':' + self.tag + ' not found');
} else {
registryUI.snackbar(this.responseText);
}
});
oReq.open('GET', registryUI.url() + '/v2/' + self.name + '/blobs/' + blob);
oReq.setRequestHeader('Accept', 'application/vnd.docker.distribution.manifest.v2+json');
oReq.send();
};
route.start(true);
</script>
</app>

View file

@ -0,0 +1,29 @@
<!--
Copyright (C) 2016-2018 Jones Magloire @Joxit
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<tag-history-button>
<a href="#" title="This will show the history of given tag" onclick="registryUI.taghistory.go('{ opts.image.name }', '{ opts.image.tag }');">
<i class="material-icons">history</i>
</a>
<script>
registryUI.taghistory.instance = this;
registryUI.taghistory.go = function (image, tag) {
route('taglist/joxit/docker-registry-ui');
};
</script>
</tag-history-button>

View file

@ -1,21 +1,89 @@
<!--
Copyright (C) 2016-2018 Jones Magloire @Joxit
Copyright (C) 2016-2018 Jones Magloire @Joxit
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<tag-history>
<a href="#" title="This will show the history of given tag" onclick="">
<i class="material-icons">history</i>
</a>
<material-card ref="tag-history-tag" class="tag-history">
<div class="material-card-title-action">
<h2>History of { registryUI.taghistory.image }:{ registryUI.taghistory.tag }</h2>
</div>
<div hide="{ registryUI.taghistory.loadend }" class="spinner-wrapper">
<material-spinner></material-spinner>
</div>
<table show="{ registryUI.taghistory.loadend }" style="border: none;">
<thead>
<tr>
<th class="material-card-th-left">One</th>
<th>Two</th>
<th>Three</th>
</tr>
</thead>
<tbody>
<tr each="{ historyelement in registryUI.taghistory.elements }">
<td class="material-card-th-left">
{ historyelement.v1Compatibility }
</td>
<td class="copy-to-clipboard">
</td>
<td>
</td>
</tr>
</tbody>
</table>
</material-card>
<script>
console.log("taghistory script area");
registryUI.taghistory.instance = this;
registryUI.taghistory.display = function () {
var oReq = new Http();
registryUI.taghistory.instance.update();
oReq.addEventListener('load', function () {
console.log("taghistory addEventListener::load");
registryUI.taghistory.elements = [];
if (this.status == 200) {
var history = JSON.parse(this.responseText).history || [];
for(historyElement in history){
historyElement
}
} else if (this.status == 404) {
registryUI.snackbar('Manifest could not be fetched', true);
} else {
registryUI.snackbar(this.responseText);
}
});
oReq.addEventListener('error', function () {
registryUI.snackbar(this.getErrorMessage(), true);
registryUI.taghistory.elements = [];
});
oReq.addEventListener('loadend', function () {
registryUI.taghistory.loadend = true;
registryUI.taghistory.instance.update();
});
console.log("Trying to create GET call with image='" + registryUI.taghistory.image + "' and tag='" + registryUI.taghistory.tag + "'")
oReq.open('GET', registryUI.url() + '/v2/' + registryUI.taghistory.image + '/manifests/' + registryUI.taghistory.tag);
oReq.send();
};
registryUI.taghistory.display();
registryUI.taghistory.instance.update();
</script>
</tag-history>