Skip to content

Commit 08f396a

Browse files
Initial commit
0 parents  commit 08f396a

30 files changed

+7852
-0
lines changed

.browserslistrc

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
> 1%
2+
last 2 versions
3+
not ie <= 8

.eslintrc.js

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
module.exports = {
2+
root: true,
3+
env: {
4+
node: true,
5+
},
6+
extends: [
7+
'plugin:vue/essential',
8+
'@vue/airbnb',
9+
],
10+
rules: {
11+
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
12+
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
13+
},
14+
parserOptions: {
15+
parser: 'babel-eslint',
16+
},
17+
};

.gitignore

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
.DS_Store
2+
node_modules
3+
/dist
4+
5+
# local env files
6+
.env.local
7+
.env.*.local
8+
9+
# Log files
10+
npm-debug.log*
11+
yarn-debug.log*
12+
yarn-error.log*
13+
14+
# Editor directories and files
15+
.idea
16+
.vscode
17+
*.suo
18+
*.ntvs*
19+
*.njsproj
20+
*.sln
21+
*.sw*

README.md

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
# dri
3+
4+
A static Docker Distribution Registry viewer.
5+
6+
## Features
7+
8+
* View repositories and tags
9+
* View tag details and sizes of tags
10+
* Delete repositories and tags (if enabled on the registry)
11+
* Optionally scan for vulnerabilities using CoreOS Clair
12+

babel.config.js

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module.exports = {
2+
presets: [
3+
'@vue/app',
4+
],
5+
};

package.json

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"name": "dri",
3+
"version": "0.1.0",
4+
"private": true,
5+
"scripts": {
6+
"serve": "vue-cli-service serve",
7+
"build": "vue-cli-service build",
8+
"lint": "vue-cli-service lint"
9+
},
10+
"dependencies": {
11+
"filesize": "^3.6.1",
12+
"normalize.css": "^8.0.0",
13+
"parse-link-header": "^1.0.1",
14+
"vue": "^2.5.17",
15+
"vue-router": "^3.0.1"
16+
},
17+
"devDependencies": {
18+
"@vue/cli-plugin-babel": "^3.0.3",
19+
"@vue/cli-plugin-eslint": "^3.0.3",
20+
"@vue/cli-service": "^3.0.3",
21+
"@vue/eslint-config-airbnb": "^3.0.3",
22+
"vue-template-compiler": "^2.5.17"
23+
}
24+
}

postcss.config.js

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module.exports = {
2+
plugins: {
3+
autoprefixer: {},
4+
},
5+
};

public/favicon.ico

1.12 KB
Binary file not shown.

public/index.html

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
6+
<meta name="viewport" content="width=device-width,initial-scale=1.0">
7+
<base href="<%= BASE_URL %>">
8+
<link rel="icon" href="/favicon.ico">
9+
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Code+Pro|Source+Sans+Pro">
10+
<title>dri</title>
11+
</head>
12+
<body>
13+
<noscript>
14+
<strong>We're sorry but dri doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
15+
</noscript>
16+
<div id="app"></div>
17+
<!-- built files will be auto injected -->
18+
</body>
19+
</html>

src/App.vue

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<template>
2+
<router-view/>
3+
</template>
4+
5+
<style>
6+
*, *::before, *::after {
7+
box-sizing: border-box;
8+
}
9+
html, body {
10+
padding: 0;
11+
margin: 0;
12+
}
13+
html {
14+
font-size: 16px;
15+
}
16+
body {
17+
font-size: 1rem;
18+
19+
font-family: 'Source Sans Pro';
20+
}
21+
pre, code, kbd, samp {
22+
font-family: 'Source Code Pro';
23+
}
24+
</style>

src/api.js

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import parseLink from 'parse-link-header';
2+
3+
import { registryAPI, repositoriesPerPage, tagsPerPage, usePortusExplore } from '@/options';
4+
5+
async function paginatable(path, n, last = null) {
6+
const url = new URL(`${await registryAPI()}${path}`);
7+
if (n) url.searchParams.append('n', n);
8+
if (last) url.searchParams.append('last', last);
9+
10+
const response = await fetch(url);
11+
let nextLast = null;
12+
if (response.headers.has('Link')) {
13+
const links = parseLink(response.headers.get('Link'));
14+
if (links && links.next) {
15+
nextLast = links.next.last;
16+
}
17+
}
18+
return Object.assign(await response.json(), { nextLast });
19+
}
20+
21+
async function get(path) {
22+
const url = new URL(`${await registryAPI()}${path}`);
23+
const response = await fetch(url);
24+
return response.json();
25+
}
26+
27+
async function head(path) {
28+
const url = new URL(`${await registryAPI()}${path}`);
29+
const response = await fetch(url, { method: 'HEAD' });
30+
return response.headers;
31+
}
32+
33+
34+
async function repos(last = null) {
35+
if (await usePortusExplore()) {
36+
// TODO: Use the Portus API when it enables anonymous access
37+
const response = await fetch(`${await registryAPI()}/explore?explore%5Bsearch%5D=`);
38+
const html = await response.text();
39+
40+
// unconventionally parse out JSON from HTML
41+
const startString = 'window.repositories = ';
42+
const endString = ';</script>';
43+
const string = html.substring(
44+
html.lastIndexOf(startString) + startString.length,
45+
html.indexOf(endString),
46+
);
47+
return JSON.parse(string);
48+
}
49+
return paginatable('/v2/_catalog', await repositoriesPerPage(), last);
50+
}
51+
52+
async function repo(name) {
53+
return get(`/v2/${name}`);
54+
}
55+
56+
async function tags(name, last = null) {
57+
return paginatable(`/v2/${name}/tags/list`, await tagsPerPage(), last);
58+
}
59+
60+
async function tag(name, ref) {
61+
return get(`/v2/${name}/manifests/${ref}`);
62+
}
63+
64+
async function blob(name, digest) {
65+
const headers = await head(`/v2/${name}/blobs/${digest}`);
66+
return {
67+
contentLength: parseInt(headers.get('Content-Length'), 10),
68+
};
69+
}
70+
71+
export {
72+
repos,
73+
repo,
74+
tags,
75+
tag,
76+
blob,
77+
};

src/components/BlobSize.vue

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<template>
2+
<LoadableText :text="size" />
3+
</template>
4+
5+
<script>
6+
import filesize from 'filesize';
7+
import { blob } from '@/api';
8+
import LoadableText from '@/components/LoadableText.vue';
9+
10+
export default {
11+
components: {
12+
LoadableText,
13+
},
14+
props: {
15+
repo: String,
16+
blob: String,
17+
},
18+
data() {
19+
return {
20+
size: '',
21+
};
22+
},
23+
async created() {
24+
const size = await blob(this.repo, this.blob);
25+
this.size = filesize(size.contentLength);
26+
},
27+
};
28+
</script>

src/components/Error.vue

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<template>
2+
<div v-if="message">{{ message }}</div>
3+
</template>
4+
5+
<script>
6+
export default {
7+
props: {
8+
message: String,
9+
},
10+
};
11+
</script>
12+
13+
<style scoped>
14+
div {
15+
text-align: center;
16+
background: #fdd;
17+
color: #800;
18+
padding: 0.5rem 0.75rem;
19+
margin: 1rem;
20+
border-radius: 0.5rem;
21+
}
22+
</style>

src/components/Layout.vue

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<template>
2+
<div class="wrapper">
3+
<header>
4+
<slot name="title" />
5+
<slot name="error" />
6+
<slot name="toolbar" />
7+
</header>
8+
<main>
9+
<div class="content">
10+
<slot />
11+
</div>
12+
</main>
13+
<footer>
14+
<p>
15+
<a :href="source">dri</a>
16+
{{ version }}
17+
by <a href="https://github.com/serverwentdown">@serverwentdown</a>
18+
</p>
19+
</footer>
20+
</div>
21+
</template>
22+
23+
<script>
24+
import { version, source } from '@/options';
25+
26+
export default {
27+
data() {
28+
return {
29+
version,
30+
source,
31+
};
32+
},
33+
};
34+
</script>
35+
36+
<style scoped>
37+
header, footer, main {
38+
margin: 0 auto;
39+
}
40+
header, footer, .content {
41+
padding: 1rem;
42+
}
43+
header, footer {
44+
max-width: 38rem;
45+
text-align: center;
46+
}
47+
main {
48+
width: fit-content;
49+
max-width: 100%;
50+
overflow-x: auto;
51+
}
52+
</style>

src/components/List.vue

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<template>
2+
<table>
3+
<thead>
4+
<slot name="header" />
5+
</thead>
6+
<tbody>
7+
<slot />
8+
</tbody>
9+
</table>
10+
</template>
11+
12+
<style scoped>
13+
table {
14+
min-width: 28rem;
15+
border-spacing: 0;
16+
}
17+
</style>

src/components/ListHeader.vue

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<template>
2+
<tr>
3+
<th class="col title"><slot name="title" /></th>
4+
<th class="col detail" v-if="$slots.detail"><slot name="detail" /></th>
5+
<th class="col date" v-if="$slots.date"><slot name="date" /></th>
6+
<th class="col size" v-if="$slots.size"><slot name="size" /></th>
7+
<th class="col buttons" v-if="$slots.buttons"><slot name="buttons" /></th>
8+
</tr>
9+
</template>
10+
11+
<style scoped>
12+
th {
13+
padding: 0.5rem;
14+
white-space: nowrap;
15+
font-size: 0.8em;
16+
text-align: left;
17+
border-bottom: 1px solid #eee;
18+
text-align: right;
19+
}
20+
.title {
21+
text-align: left;
22+
}
23+
</style>

0 commit comments

Comments
 (0)