Skip to content

Commit 8228346

Browse files
Switch to tabs
1 parent 22c6819 commit 8228346

21 files changed

+758
-367
lines changed

.eslintrc.js

+17-15
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
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-
},
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+
indent: ['error', 'tab'],
14+
'no-tabs': 'off',
15+
},
16+
parserOptions: {
17+
parser: 'babel-eslint',
18+
},
1719
};

babel.config.js

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

package.json

+18-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
{
22
"name": "dri",
33
"version": "0.1.0",
4-
"private": true,
54
"scripts": {
65
"serve": "vue-cli-service serve",
76
"build": "vue-cli-service build",
@@ -19,6 +18,23 @@
1918
"@vue/cli-plugin-eslint": "^3.0.3",
2019
"@vue/cli-service": "^3.0.3",
2120
"@vue/eslint-config-airbnb": "^3.0.3",
22-
"vue-template-compiler": "^2.5.17"
21+
"vue-template-compiler": "^2.5.17",
22+
"lint-staged": "^6.0.0"
23+
},
24+
"gitHooks": {
25+
"pre-commit": "lint-staged"
26+
},
27+
"lint-staged": {
28+
"Dockerfile": [
29+
"hadolint"
30+
],
31+
"*.js": [
32+
"vue-cli-service lint",
33+
"git add"
34+
],
35+
"*.vue": [
36+
"vue-cli-service lint",
37+
"git add"
38+
]
2339
}
2440
}

postcss.config.js

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

src/api.js

+133-46
Original file line numberDiff line numberDiff line change
@@ -2,76 +2,163 @@ import parseLink from 'parse-link-header';
22

33
import { registryAPI, repositoriesPerPage, tagsPerPage, usePortusExplore } from '@/options';
44

5+
function parseWWWAuthenticate(text) {
6+
const result = {};
7+
// extract auth-scheme
8+
const schemeParts = text.split(' ');
9+
[result.scheme] = schemeParts;
10+
// extract auth-params
11+
const remain = schemeParts.slice(1).join(' ');
12+
const parts = remain.split(/ ?, ?/);
13+
parts.forEach((part) => {
14+
// parse auth-param
15+
const kv = part.split('=');
16+
if (kv.length === 2) {
17+
const key = kv[0].trim();
18+
const value = kv[1].trim();
19+
if (value.startsWith('"') && value.endsWith('"')) {
20+
result[key] = value.substring(1, value.length - 1);
21+
} else {
22+
result[key] = value;
23+
}
24+
} else {
25+
result[parts] = true;
26+
}
27+
});
28+
return result;
29+
}
30+
31+
let cachedToken = null;
32+
async function doAuth() {
33+
if (cachedToken !== null) {
34+
return cachedToken;
35+
}
36+
37+
// try accessing registry API
38+
const response = await fetch(`${await registryAPI()}/v2/`);
39+
if (response.ok) {
40+
// token not needed for registry
41+
cachedToken = false;
42+
return cachedToken;
43+
}
44+
if (response.status !== 401) {
45+
throw new Error(response.statusText);
46+
}
47+
48+
// solve authentication challenge
49+
const { headers } = response;
50+
if (!headers.has('Www-Authenticate')) {
51+
throw new Error('no challenge presented');
52+
}
53+
const chal = parseWWWAuthenticate(headers.get('Www-Authenticate'));
54+
if (chal.scheme !== 'Bearer') {
55+
throw new Error('unsupported scheme');
56+
}
57+
58+
const tokURL = new URL(chal.realm);
59+
tokURL.searchParams.append('client_id', 'dri-client');
60+
tokURL.searchParams.append('service', chal.service);
61+
tokURL.searchParams.append('scope', 'repository:pwd/migrate:pull');
62+
const tokResponse = await fetch(tokURL);
63+
const tok = await tokResponse.json();
64+
cachedToken = tok.token;
65+
return tok.token;
66+
}
67+
568
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 });
69+
const token = await doAuth();
70+
const url = new URL(`${await registryAPI()}${path}`);
71+
if (n) url.searchParams.append('n', n);
72+
if (last) url.searchParams.append('last', last);
73+
74+
const headers = {};
75+
if (token) headers.Authorization = `Bearer ${token}`;
76+
const response = await fetch(url, { headers });
77+
let nextLast = null;
78+
if (response.headers.has('Link')) {
79+
const links = parseLink(response.headers.get('Link'));
80+
if (links && links.next) {
81+
nextLast = links.next.last;
82+
}
83+
}
84+
return Object.assign(await response.json(), { nextLast });
1985
}
2086

2187
async function get(path) {
22-
const url = new URL(`${await registryAPI()}${path}`);
23-
const response = await fetch(url);
24-
return response.json();
88+
const token = await doAuth();
89+
const url = new URL(`${await registryAPI()}${path}`);
90+
const headers = {};
91+
if (token) headers.Authorization = `Bearer ${token}`;
92+
const response = await fetch(url, { headers });
93+
return response.json();
2594
}
2695

2796
async function head(path) {
28-
const url = new URL(`${await registryAPI()}${path}`);
29-
const response = await fetch(url, { method: 'HEAD' });
30-
return response.headers;
97+
const token = await doAuth();
98+
const url = new URL(`${await registryAPI()}${path}`);
99+
const headers = {};
100+
if (token) headers.Authorization = `Bearer ${token}`;
101+
const response = await fetch(url, { method: 'HEAD', headers });
102+
return response.headers;
31103
}
32104

33105

106+
async function portus() {
107+
// TODO: Use the Portus API when it enables anonymous access
108+
const response = await fetch(`${await registryAPI()}/explore?explore%5Bsearch%5D=`);
109+
const html = await response.text();
110+
111+
// unconventionally parse out JSON from HTML
112+
const startString = 'window.repositories = ';
113+
const endString = ';</script>';
114+
const string = html.substring(
115+
html.lastIndexOf(startString) + startString.length,
116+
html.indexOf(endString),
117+
);
118+
const object = JSON.parse(string);
119+
return object;
120+
}
121+
34122
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);
123+
if (await usePortusExplore()) {
124+
const p = await portus();
125+
return {
126+
repositories: p.map(r => r.full_name),
127+
};
128+
}
129+
return paginatable('/v2/_catalog', await repositoriesPerPage(), last);
50130
}
51131

52132
async function repo(name) {
53-
return get(`/v2/${name}`);
133+
return get(`/v2/${name}`);
54134
}
55135

56136
async function tags(name, last = null) {
57-
return paginatable(`/v2/${name}/tags/list`, await tagsPerPage(), last);
137+
if (await usePortusExplore()) {
138+
const p = await portus();
139+
return {
140+
tags: p.find(r => r.full_name === name)
141+
.tags.map(t => t[0].name),
142+
};
143+
}
144+
return paginatable(`/v2/${name}/tags/list`, await tagsPerPage(), last);
58145
}
59146

60147
async function tag(name, ref) {
61-
return get(`/v2/${name}/manifests/${ref}`);
148+
return get(`/v2/${name}/manifests/${ref}`);
62149
}
63150

64151
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-
};
152+
const headers = await head(`/v2/${name}/blobs/${digest}`);
153+
return {
154+
contentLength: parseInt(headers.get('Content-Length'), 10),
155+
};
69156
}
70157

71158
export {
72-
repos,
73-
repo,
74-
tags,
75-
tag,
76-
blob,
159+
repos,
160+
repo,
161+
tags,
162+
tag,
163+
blob,
77164
};

src/components/BlobSize.vue

+16-16
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,21 @@ import { blob } from '@/api';
88
import LoadableText from '@/components/LoadableText.vue';
99
1010
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-
},
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+
},
2727
};
2828
</script>

src/components/Error.vue

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44

55
<script>
66
export default {
7-
props: {
8-
message: String,
9-
},
7+
props: {
8+
message: String,
9+
},
1010
};
1111
</script>
1212

src/components/Layout.vue

+6-6
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@
2424
import { version, source } from '@/options';
2525
2626
export default {
27-
data() {
28-
return {
29-
version,
30-
source,
31-
};
32-
},
27+
data() {
28+
return {
29+
version,
30+
source,
31+
};
32+
},
3333
};
3434
</script>
3535

src/components/ListItem.vue

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010

1111
<script>
1212
export default {
13-
props: {
14-
to: { type: [Object, String], required: false },
15-
},
13+
props: {
14+
to: { type: [Object, String], required: false },
15+
},
1616
};
1717
</script>
1818

src/components/LoadableText.vue

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44

55
<script>
66
export default {
7-
props: {
8-
text: String,
9-
},
7+
props: {
8+
text: String,
9+
},
1010
};
1111
</script>
1212

0 commit comments

Comments
 (0)