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