Skip to content

Commit 6753d4f

Browse files
committed
feat(new tool): Docker Image Downloader
Fix #298
1 parent c56cc39 commit 6753d4f

2 files changed

Lines changed: 170 additions & 0 deletions

File tree

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
<script setup lang="ts">
2+
import { useITStorage, useQueryParamOrStorage } from '@/composable/queryParams';
3+
4+
const message = useMessage();
5+
const notification = useNotification();
6+
7+
const username = ref('');
8+
const password = ref('');
9+
10+
const image = useQueryParamOrStorage({ name: 'image', storageName: 'docker-dl:i', defaultValue: '' });
11+
const platform = useQueryParamOrStorage({ name: 'platform', storageName: 'docker-dl:p', defaultValue: '' });
12+
const registry = useQueryParamOrStorage({ name: 'registry', storageName: 'docker-dl:r', defaultValue: '' });
13+
const serverHost = useITStorage('docker-dl:url', 'http://localhost:3000');
14+
15+
const loading = ref(false);
16+
const error = ref<string | null>(null);
17+
18+
// Platform options
19+
const platformOptions = [
20+
{ label: 'linux/amd64', value: 'linux/amd64' },
21+
{ label: 'linux/arm64', value: 'linux/arm64' },
22+
{ label: 'linux/arm/v7', value: 'linux/arm/v7' },
23+
{ label: 'linux/arm/v6', value: 'linux/arm/v6' },
24+
{ label: 'linux/ppc64le', value: 'linux/ppc64le' },
25+
{ label: 'linux/s390x', value: 'linux/s390x' },
26+
{ label: 'windows/amd64', value: 'windows/amd64' },
27+
];
28+
29+
async function downloadImage() {
30+
error.value = null;
31+
32+
if (!image.value) {
33+
error.value = 'Image name is required.';
34+
return;
35+
}
36+
37+
loading.value = true;
38+
39+
try {
40+
const params = new URLSearchParams();
41+
params.append('image', image.value);
42+
43+
if (platform.value) {
44+
params.append('platform', platform.value);
45+
}
46+
if (registry.value) {
47+
params.append('registry', registry.value);
48+
}
49+
if (username.value) {
50+
params.append('username', username.value);
51+
}
52+
if (password.value) {
53+
params.append('password', password.value);
54+
}
55+
56+
const url = `${serverHost.value}/download?${params.toString()}`;
57+
58+
const response = await fetch(url);
59+
60+
if (!response.ok) {
61+
const text = await response.text();
62+
throw new Error(text || 'Server error');
63+
}
64+
65+
// Extract filename from Content-Disposition
66+
const disposition = response.headers.get('Content-Disposition');
67+
const filename
68+
= disposition?.match(/filename="(.+)"/)?.[1]
69+
|| `${image.value.replace(/[/:]/g, '_')}.tar`;
70+
71+
// Download file
72+
const blob = await response.blob();
73+
const link = document.createElement('a');
74+
link.href = URL.createObjectURL(blob);
75+
link.download = filename;
76+
link.click();
77+
78+
notification.success({
79+
title: 'Download started',
80+
description: `Downloading ${filename}`,
81+
});
82+
}
83+
catch (err: any) {
84+
error.value = err.message || 'Unknown error';
85+
message.error(error.value!);
86+
}
87+
finally {
88+
loading.value = false;
89+
}
90+
}
91+
</script>
92+
93+
<template>
94+
<div>
95+
<NForm label-width="120px" label-placement="left">
96+
<details mb-2>
97+
<summary>Docker Image Download Service</summary>
98+
<n-card>
99+
<NFormItem label="Docker Image Download Service Url:" label-placement="top">
100+
<NInput v-model:value="serverHost" placeholder="http://localhost:3000" />
101+
</NFormItem>
102+
<n-p>
103+
You must self host Docker Image Download Service. See:
104+
<c-link href="https://github.com/sharevb/docker-image-download-server?tab=readme-ov-file#running-in-docker" target="_blank">
105+
Docker Image Download Service install
106+
</c-link>
107+
</n-p>
108+
</n-card>
109+
</details>
110+
111+
<NFormItem label="Docker Image:">
112+
<NInput v-model:value="image" placeholder="alpine:latest" />
113+
</NFormItem>
114+
115+
<NFormItem label="Platform:">
116+
<NSelect
117+
v-model:value="platform"
118+
:options="platformOptions"
119+
clearable
120+
/>
121+
</NFormItem>
122+
123+
<NFormItem label="Registry Url:">
124+
<NInput v-model:value="registry" placeholder="myregistry.com (optional)" />
125+
</NFormItem>
126+
127+
<NFormItem label="Username:">
128+
<NInput v-model:value="username" placeholder="Optional" />
129+
</NFormItem>
130+
131+
<NFormItem label="Password:">
132+
<NInput
133+
v-model:value="password"
134+
type="password"
135+
show-password-on="click"
136+
/>
137+
</NFormItem>
138+
</NForm>
139+
140+
<n-space justify="center">
141+
<NButton
142+
type="primary"
143+
:loading="loading"
144+
@click="downloadImage"
145+
>
146+
Download Image
147+
</NButton>
148+
</n-space>
149+
150+
<div v-if="error" mt-2>
151+
<NAlert type="error" title="Error" :bordered="false">
152+
{{ error }}
153+
</NAlert>
154+
</div>
155+
</div>
156+
</template>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { FileDownload } from '@vicons/tabler';
2+
import { defineTool } from '../tool';
3+
4+
export const tool = defineTool({
5+
name: 'Docker Image Downloader',
6+
path: '/docker-image-downloader',
7+
description: 'Download a docker image given a docker registry, an architecture and an image name',
8+
keywords: ['docker', 'image', 'downloader'],
9+
component: () => import('./docker-image-downloader.vue'),
10+
icon: FileDownload,
11+
createdAt: new Date('2026-01-18'),
12+
category: 'Docker',
13+
externAccessDescription: 'This tool downloads Docker Images from registry (and with username, password or token you provide)',
14+
});

0 commit comments

Comments
 (0)