mirror of
https://github.com/Joxit/docker-registry-ui.git
synced 2025-04-27 23:50:01 +03:00
feat(branching): add configuration for catalog arborescence
This commit is contained in:
parent
398fa65fa1
commit
1031034bc4
11 changed files with 80 additions and 43 deletions
|
@ -13,6 +13,8 @@ sed -i "s~\${HISTORY_CUSTOM_LABELS}~${HISTORY_CUSTOM_LABELS}~" index.html
|
||||||
sed -i "s~\${USE_CONTROL_CACHE_HEADER}~${USE_CONTROL_CACHE_HEADER}~" index.html
|
sed -i "s~\${USE_CONTROL_CACHE_HEADER}~${USE_CONTROL_CACHE_HEADER}~" index.html
|
||||||
sed -i "s~\${TAGLIST_ORDER}~${TAGLIST_ORDER}~" index.html
|
sed -i "s~\${TAGLIST_ORDER}~${TAGLIST_ORDER}~" index.html
|
||||||
sed -i "s~\${CATALOG_DEFAULT_EXPANDED}~${CATALOG_DEFAULT_EXPANDED}~" index.html
|
sed -i "s~\${CATALOG_DEFAULT_EXPANDED}~${CATALOG_DEFAULT_EXPANDED}~" index.html
|
||||||
|
sed -i "s~\${CATALOG_MIN_BRANCHES}~${CATALOG_MIN_BRANCHES}~" index.html
|
||||||
|
sed -i "s~\${CATALOG_MAX_BRANCHES}~${CATALOG_MAX_BRANCHES}~" index.html
|
||||||
|
|
||||||
grep -o 'THEME[A-Z_]*' index.html | while read e; do
|
grep -o 'THEME[A-Z_]*' index.html | while read e; do
|
||||||
sed -i "s~\${$e}~$(printenv $e)~" index.html
|
sed -i "s~\${$e}~$(printenv $e)~" index.html
|
||||||
|
|
|
@ -52,6 +52,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
show-catalog-nb-tags="{ props.showCatalogNbTags }"
|
show-catalog-nb-tags="{ props.showCatalogNbTags }"
|
||||||
class="animated {!state.expanded && !props.filterResults ? 'hide' : ''} {state.expanding ? 'expanding' : ''}"
|
class="animated {!state.expanded && !props.filterResults ? 'hide' : ''} {state.expanding ? 'expanding' : ''}"
|
||||||
each="{item in state.images}"
|
each="{item in state.images}"
|
||||||
|
z-index="{ props.zIndex - 1 }"
|
||||||
item="{ item }"
|
item="{ item }"
|
||||||
></catalog-element>
|
></catalog-element>
|
||||||
</div>
|
</div>
|
||||||
|
@ -76,6 +77,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
this.getNbTags(props, state);
|
this.getNbTags(props, state);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
onMounted(props, state) {
|
||||||
|
const materialCard = this.$('material-card');
|
||||||
|
if (materialCard) {
|
||||||
|
materialCard.style['z-index'] = props.zIndex;
|
||||||
|
}
|
||||||
|
},
|
||||||
onBeforeUpdate(props, state) {
|
onBeforeUpdate(props, state) {
|
||||||
if (props.filterResults && state.images) {
|
if (props.filterResults && state.images) {
|
||||||
state.nImages = state.images.filter((image) => matchSearch(props.filterResults, image)).length;
|
state.nImages = state.images.filter((image) => matchSearch(props.filterResults, image)).length;
|
||||||
|
|
|
@ -35,10 +35,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
on-authentication="{ props.onAuthentication }"
|
on-authentication="{ props.onAuthentication }"
|
||||||
show-catalog-nb-tags="{ props.showCatalogNbTags }"
|
show-catalog-nb-tags="{ props.showCatalogNbTags }"
|
||||||
catalog-default-expanded="{ props.catalogDefaultExpanded || state.nRepositories === 1 }"
|
catalog-default-expanded="{ props.catalogDefaultExpanded || state.nRepositories === 1 }"
|
||||||
|
z-index="{ props.catalogMaxBranches - props.catalogMinBranches + 2 }"
|
||||||
></catalog-element>
|
></catalog-element>
|
||||||
<script>
|
<script>
|
||||||
import CatalogElement from './catalog-element.riot';
|
import CatalogElement from './catalog-element.riot';
|
||||||
import { Http } from '../../scripts/http';
|
import { Http } from '../../scripts/http';
|
||||||
|
import { getBranching } from '../../scripts/repositories';
|
||||||
import { getRegistryServers } from '../../scripts/utils';
|
import { getRegistryServers } from '../../scripts/utils';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -56,6 +58,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
onBeforeMount(props) {
|
onBeforeMount(props) {
|
||||||
this.state.registryName = props.registryName;
|
this.state.registryName = props.registryName;
|
||||||
this.state.catalogElementsLimit = props.catalogElementsLimit;
|
this.state.catalogElementsLimit = props.catalogElementsLimit;
|
||||||
|
try {
|
||||||
|
this.state.branching = getBranching(props.catalogMinBranches, props.catalogMaxBranches);
|
||||||
|
} catch (e) {
|
||||||
|
props.onNotify(e);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onMounted(props, state) {
|
onMounted(props, state) {
|
||||||
this.display(props, state);
|
this.display(props, state);
|
||||||
|
@ -69,6 +76,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
}
|
}
|
||||||
state.registryUrl = props.registryUrl;
|
state.registryUrl = props.registryUrl;
|
||||||
let repositories = [];
|
let repositories = [];
|
||||||
|
let nImages = 0;
|
||||||
const self = this;
|
const self = this;
|
||||||
const catalogUrl = `${props.registryUrl}/v2/_catalog?n=${state.catalogElementsLimit}`;
|
const catalogUrl = `${props.registryUrl}/v2/_catalog?n=${state.catalogElementsLimit}`;
|
||||||
const oReq = new Http({
|
const oReq = new Http({
|
||||||
|
@ -78,22 +86,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
if (this.status === 200) {
|
if (this.status === 200) {
|
||||||
repositories = JSON.parse(this.responseText).repositories || [];
|
repositories = JSON.parse(this.responseText).repositories || [];
|
||||||
repositories.sort();
|
repositories.sort();
|
||||||
repositories = repositories.reduce(function (acc, e) {
|
nImages = repositories.length;
|
||||||
const slash = e.indexOf('/');
|
if (typeof state.branching === 'function') {
|
||||||
if (slash > 0) {
|
repositories = state.branching(repositories);
|
||||||
const repoName = e.substring(0, slash) + '/';
|
}
|
||||||
if (acc.length === 0 || acc[acc.length - 1].repo != repoName) {
|
|
||||||
acc.push({
|
|
||||||
repo: repoName,
|
|
||||||
images: [],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
acc[acc.length - 1].images.push(e);
|
|
||||||
return acc;
|
|
||||||
}
|
|
||||||
acc.push(e);
|
|
||||||
return acc;
|
|
||||||
}, []);
|
|
||||||
} else if (this.status === 404) {
|
} else if (this.status === 404) {
|
||||||
self.props.onNotify({ code: 'CATALOG_NOT_FOUND', url: catalogUrl }, true);
|
self.props.onNotify({ code: 'CATALOG_NOT_FOUND', url: catalogUrl }, true);
|
||||||
} else if (this.status === 400) {
|
} else if (this.status === 400) {
|
||||||
|
@ -116,7 +112,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
self.update({
|
self.update({
|
||||||
repositories,
|
repositories,
|
||||||
nRepositories: repositories.length,
|
nRepositories: repositories.length,
|
||||||
nImages: repositories.reduce((acc, e) => acc + ((e.images && e.images.length) || 1), 0),
|
nImages,
|
||||||
loadend: true,
|
loadend: true,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -125,4 +121,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
<style>
|
||||||
|
catalog {
|
||||||
|
display: block;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
catalog > material-card {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</catalog>
|
</catalog>
|
||||||
|
|
|
@ -31,6 +31,21 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
</material-navbar>
|
</material-navbar>
|
||||||
</header>
|
</header>
|
||||||
<main>
|
<main>
|
||||||
|
<error-page
|
||||||
|
if="{ state.pageError && !Array.isArray(state.pageError.errors) }"
|
||||||
|
code="{ state.pageError.code }"
|
||||||
|
status="{ state.pageError.status }"
|
||||||
|
message="{ state.pageError.message }"
|
||||||
|
url="{ state.pageError.url }"
|
||||||
|
></error-page>
|
||||||
|
<error-page
|
||||||
|
if="{ state.pageError && Array.isArray(state.pageError.errors) }"
|
||||||
|
each="{ error in (state.pageError && state.pageError.errors) }"
|
||||||
|
code="{ error.code }"
|
||||||
|
detail="{ error.detail }"
|
||||||
|
message="{ error.message }"
|
||||||
|
url="{ state.pageError.url }"
|
||||||
|
></error-page>
|
||||||
<router base="#!">
|
<router base="#!">
|
||||||
<route path="{baseRoute}">
|
<route path="{baseRoute}">
|
||||||
<catalog
|
<catalog
|
||||||
|
@ -42,6 +57,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
on-authentication="{ onAuthentication }"
|
on-authentication="{ onAuthentication }"
|
||||||
show-catalog-nb-tags="{ truthy(props.showCatalogNbTags) }"
|
show-catalog-nb-tags="{ truthy(props.showCatalogNbTags) }"
|
||||||
catalog-default-expanded="{ truthy(props.catalogDefaultExpanded) }"
|
catalog-default-expanded="{ truthy(props.catalogDefaultExpanded) }"
|
||||||
|
catalog-min-branches="{ props.catalogMinBranches }"
|
||||||
|
catalog-max-branches="{ props.catalogMaxBranches }"
|
||||||
></catalog>
|
></catalog>
|
||||||
</route>
|
</route>
|
||||||
<route path="{baseRoute}taglist/(.*)">
|
<route path="{baseRoute}taglist/(.*)">
|
||||||
|
@ -82,21 +99,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
on-authenticated="{ state.onAuthenticated }"
|
on-authenticated="{ state.onAuthenticated }"
|
||||||
opened="{ state.authenticationDialogOpened }"
|
opened="{ state.authenticationDialogOpened }"
|
||||||
></registry-authentication>
|
></registry-authentication>
|
||||||
<error-page
|
|
||||||
if="{ state.pageError && !Array.isArray(state.pageError.errors) }"
|
|
||||||
code="{ state.pageError.code }"
|
|
||||||
status="{ state.pageError.status }"
|
|
||||||
message="{ state.pageError.message }"
|
|
||||||
url="{ state.pageError.url }"
|
|
||||||
></error-page>
|
|
||||||
<error-page
|
|
||||||
if="{ state.pageError && Array.isArray(state.pageError.errors) }"
|
|
||||||
each="{ error in (state.pageError && state.pageError.errors) }"
|
|
||||||
code="{ error.code }"
|
|
||||||
detail="{ error.detail }"
|
|
||||||
message="{ error.message }"
|
|
||||||
url="{ state.pageError.url }"
|
|
||||||
></error-page>
|
|
||||||
<material-snackbar message="{ state.snackbarMessage }" is-error="{ state.snackbarIsError }"></material-snackbar>
|
<material-snackbar message="{ state.snackbarMessage }" is-error="{ state.snackbarIsError }"></material-snackbar>
|
||||||
</main>
|
</main>
|
||||||
<footer>
|
<footer>
|
||||||
|
|
|
@ -61,6 +61,10 @@
|
||||||
<a href="https://github.com/Joxit/docker-registry-ui/issues/306">Joxit/docker-registry-ui#306</a>.
|
<a href="https://github.com/Joxit/docker-registry-ui/issues/306">Joxit/docker-registry-ui#306</a>.
|
||||||
</p>
|
</p>
|
||||||
</template>
|
</template>
|
||||||
|
<template if="{ props.code === 'CATALOG_BRANCHING_CONFIGURATION' }">
|
||||||
|
<p>Wrong configuration for the branching feature: { props.message }</p>
|
||||||
|
<p>Configuration environment variables are : CATALOG_MIN_BRANCH and CATALOG_MAX_BRANCH</p>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
|
|
|
@ -159,7 +159,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
return router.taglist(this.props.image);
|
return router.taglist(this.props.image);
|
||||||
},
|
},
|
||||||
showDockerfile() {
|
showDockerfile() {
|
||||||
console.log(this);
|
|
||||||
this.update({ showDockerfile: true });
|
this.update({ showDockerfile: true });
|
||||||
},
|
},
|
||||||
onDockerfileClose() {
|
onDockerfileClose() {
|
||||||
|
|
|
@ -49,6 +49,8 @@
|
||||||
use-control-cache-header="${USE_CONTROL_CACHE_HEADER}"
|
use-control-cache-header="${USE_CONTROL_CACHE_HEADER}"
|
||||||
taglist-order="${TAGLIST_ORDER}"
|
taglist-order="${TAGLIST_ORDER}"
|
||||||
catalog-default-expanded="${CATALOG_DEFAULT_EXPANDED}"
|
catalog-default-expanded="${CATALOG_DEFAULT_EXPANDED}"
|
||||||
|
catalog-min-branches="${CATALOG_MIN_BRANCHES}"
|
||||||
|
catalog-max-branches="${CATALOG_MAX_BRANCHES}"
|
||||||
theme="${THEME}"
|
theme="${THEME}"
|
||||||
theme-primary-text="${THEME_PRIMARY_TEXT}"
|
theme-primary-text="${THEME_PRIMARY_TEXT}"
|
||||||
theme-neutral-text="${THEME_NEUTRAL_TEXT}"
|
theme-neutral-text="${THEME_NEUTRAL_TEXT}"
|
||||||
|
@ -77,6 +79,8 @@
|
||||||
use-control-cache-header="false"
|
use-control-cache-header="false"
|
||||||
taglist-order=""
|
taglist-order=""
|
||||||
catalog-default-expanded=""
|
catalog-default-expanded=""
|
||||||
|
catalog-min-branches="1"
|
||||||
|
catalog-max-branches="1"
|
||||||
theme="auto"
|
theme="auto"
|
||||||
theme-primary-text=""
|
theme-primary-text=""
|
||||||
theme-neutral-text=""
|
theme-neutral-text=""
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
export class DockerRegistryUIError extends Error {
|
export class DockerRegistryUIError extends Error {
|
||||||
constructor(msg) {
|
constructor(msg, code) {
|
||||||
super(msg);
|
super(msg);
|
||||||
this.isError = true;
|
this.isError = true;
|
||||||
|
this.code = code;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { DockerRegistryUIError } from './error.js';
|
import { DockerRegistryUIError } from './error.js';
|
||||||
|
const ERROR_CODE = 'CATALOG_BRANCHING_CONFIGURATION';
|
||||||
|
|
||||||
const getRepositoryName = (split, max) => {
|
const getRepositoryName = (split, max) => {
|
||||||
let repositoryName = '';
|
let repositoryName = '';
|
||||||
|
@ -29,14 +30,24 @@ const getLatestRepository = (repo, repoName) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const cleanInt = (n) => (n === '' ? 1 : parseInt(n));
|
||||||
|
|
||||||
export const getBranching = (min = 1, max = 1) => {
|
export const getBranching = (min = 1, max = 1) => {
|
||||||
if (min > max) {
|
min = cleanInt(min);
|
||||||
throw new DockerRegistryUIError(`min must be inferior to max (min: ${min} <= max: ${max})`);
|
max = cleanInt(max);
|
||||||
|
if (isNaN(min) || isNaN(max)) {
|
||||||
|
throw new DockerRegistryUIError(`min and max must be integers: (min: ${min} and max: ${max}))`, ERROR_CODE);
|
||||||
|
} else if (min > max) {
|
||||||
|
throw new DockerRegistryUIError(`min must be inferior to max (min: ${min} <= max: ${max})`, ERROR_CODE);
|
||||||
} else if (max < 0 || min < 0) {
|
} else if (max < 0 || min < 0) {
|
||||||
throw new DockerRegistryUIError(
|
throw new DockerRegistryUIError(
|
||||||
`min and max must be greater than equals to 0 (min: ${min} >= 0 and max: ${max} >= 0)`
|
`min and max must be greater than equals to 0 (min: ${min} >= 0 and max: ${max} >= 0)`,
|
||||||
|
ERROR_CODE
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (max == 1) {
|
||||||
|
min = 1;
|
||||||
|
}
|
||||||
return (repositories) =>
|
return (repositories) =>
|
||||||
repositories.sort().reduce(function (acc, image) {
|
repositories.sort().reduce(function (acc, image) {
|
||||||
const split = image.split('/');
|
const split = image.split('/');
|
||||||
|
|
|
@ -69,17 +69,18 @@ material-card {
|
||||||
material-card,
|
material-card,
|
||||||
material-tabs,
|
material-tabs,
|
||||||
pagination .container {
|
pagination .container {
|
||||||
max-width: 95%;
|
|
||||||
margin: auto;
|
margin: auto;
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 1515px * 0.95 = 1440px */
|
material-card,
|
||||||
@media screen and (min-width: 1515px) {
|
material-tabs,
|
||||||
material-card,
|
pagination .container,
|
||||||
material-tabs,
|
catalog {
|
||||||
pagination .container {
|
max-width: 95%;
|
||||||
|
/* 1515px * 0.95 = 1440px */
|
||||||
|
@media screen and (min-width: 1515px) {
|
||||||
max-width: 1440px;
|
max-width: 1440px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,5 +117,6 @@ describe('repositories', () => {
|
||||||
assert.throws(() => getBranching(2, 1), DockerRegistryUIError, `Did not throw on min > max`);
|
assert.throws(() => getBranching(2, 1), DockerRegistryUIError, `Did not throw on min > max`);
|
||||||
assert.throws(() => getBranching(-2, 1), DockerRegistryUIError, `Did not throw on min < 0`);
|
assert.throws(() => getBranching(-2, 1), DockerRegistryUIError, `Did not throw on min < 0`);
|
||||||
assert.throws(() => getBranching(2, -1), DockerRegistryUIError, `Did not throw on max < 0`);
|
assert.throws(() => getBranching(2, -1), DockerRegistryUIError, `Did not throw on max < 0`);
|
||||||
|
assert.throws(() => getBranching('foo', 'bar'), DockerRegistryUIError, `Did not throw on max < 0`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue