diff --git a/src/lib/ServiceStats.svelte b/src/lib/ServiceStats.svelte index 1944dae..7fae600 100644 --- a/src/lib/ServiceStats.svelte +++ b/src/lib/ServiceStats.svelte @@ -7,123 +7,107 @@
{#if template.type} -
- Type - {#if template.type === 1} - Container - {:else if template.type === 2} - Swarm - {:else if template.type === 3} - Kubernetes - {:else} - Unknown - {/if} -
+ Type + {#if template.type === 1} + Container + {:else if template.type === 2} + Swarm + {:else if template.type === 3} + Kubernetes + {:else} + Unknown + {/if} {/if} {#if template.platform} -
- Platform - {template.platform} -
+ Platform + {template.platform} {/if} {#if template.image} -
- Image - {template.image} -
+ Image + {template.image} {/if} {#if template.command} -
- Command - {template.command} -
+ Command + {template.command} {/if} {#if typeof template.interactive === 'boolean'} -
- Interactive - {template.interactive ? 'Yes' : 'No'} -
+ Interactive + {template.interactive ? 'Yes' : 'No'} {/if} {#if template.ports} -
- Ports -

- {#each template.ports as port}{port}{/each} -

-
+ Ports +

+ {#each template.ports as port}{port}{/each} +

{/if} {#if template.volumes} -
- Volumes -

- {#each template.volumes as volume}{volume.container || volume}{/each} -

-
+ Volumes +

+ {#each template.volumes as volume} + + {volume.container || volume}{volume?.bind? ' : ' + volume.bind : ''} + {/each} +

{/if} {#if template.restart_policy} -
- Restart Policy - {template.restart_policy} -
+ Restart Policy + {template.restart_policy} {/if} {#if template.repository} -
Sourced - Repo -
+ Repo {/if} {#if template.entrypoint} -
Entrypoint - {template.entrypoint} -
+ {template.entrypoint} {/if} {#if template.build} -
Build - {template.build} -
+ {template.build} {/if} {#if template.env} -
Env Vars -

- {#each template.env as env}{env.name}={env.set || env.value || env.default}{/each} +

+ {#each template.env as env}{env.name}={env.set || env.value || env.default || '\'\''}{/each}

-
{/if}
diff --git a/src/lib/TemplateNotFound.svelte b/src/lib/TemplateNotFound.svelte index 11805d7..54ae4ff 100644 --- a/src/lib/TemplateNotFound.svelte +++ b/src/lib/TemplateNotFound.svelte @@ -1 +1,60 @@ -

Temp no Found

\ No newline at end of file + + +
+

Template not Found 😢

+

It doesn't look like there was a templated named "{templateName}"

+

+ You can try searching for another, or if you think there's a mistake somewhere, + please open an issue on the Github Repo. +

+ Back Home +
+ + \ No newline at end of file diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 93c85a3..4a24515 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -1,5 +1,4 @@ - \ No newline at end of file diff --git a/src/routes/[slug]/+page.server.ts b/src/routes/[slug]/+page.server.ts index c772f88..b3e9370 100644 --- a/src/routes/[slug]/+page.server.ts +++ b/src/routes/[slug]/+page.server.ts @@ -1,18 +1,107 @@ -import { get } from 'svelte/store'; +import yaml from 'js-yaml'; +import { get } from 'svelte/store'; import { templatesUrl } from '$src/constants'; import { templates } from '$src/store'; -export const load = async () => { - if (get(templates) && get(templates).length > 0) { - return { - templates: get(templates), - } - } else { - const data = await fetch(templatesUrl).then((res) => res.json()); - templates.set(data.templates); - return { - templates: data.templates, +/* Based on the current page name, find the corresponding template */ +const findTemplate = (templates: any, slug: string) => { + return templates.find((temp: Template) => + temp.title.toLowerCase().replace(/[^a-zA-Z ]/g, "").replaceAll(' ', '-') === slug + ); +}; + +/* With a given image name, fetch stats from DockerHub registry */ +const getDockerHubStats = async (image: string): Promise => { + if (!image) return null; + const [imageName, tag] = image.split(':'); + const [namespace, repo] = imageName.includes('/') ? imageName.split('/') : ['library', imageName]; + const apiEndpoint = `https://hub.docker.com/v2/repositories/${namespace}/${repo}/`; + + return await fetch(apiEndpoint) + .then((res) => res.json()) + .then((data) => { + return data; + }) + .catch((err) => { + return null; + }); +} +const getServices = async (template): Promise => { + try { + if (template?.repository) { + const { url: repoUrl, stackfile } = template.repository; + const path = `${repoUrl.replace('github.com', 'raw.githubusercontent.com')}/HEAD/${stackfile}`; + const response = await fetch(path); + const data = await response.text(); + const parsedData = yaml.load(data); + const someServices: Service[] = []; + if (!parsedData.services) return []; + + Object.keys(parsedData.services).forEach((service) => { + const serviceData = parsedData.services[service]; + someServices.push({ + name: service, + image: serviceData.image, + entrypoint: serviceData.entrypoint, + command: serviceData.command, + ports: serviceData.ports, + build: serviceData.build, + interactive: serviceData.interactive, + volumes: serviceData.volumes?.map((vol) => ({ + bind: vol.split(':')[0], + container: vol.split(':')[1], + })), + restart_policy: serviceData.restart, + env: Object.keys(serviceData.environment || {}).map((envName) => ({ + name: envName, + value: serviceData.environment[envName], + })), + }); + }); + return someServices; + } else { + return []; } + } catch (error) { + console.error('Error fetching or parsing YAML:', error); + return []; + } +}; + +/* Format results for returning to component */ +const returnResults = async (templates, templateSlug) => { + // Find template, based on slug + let template = findTemplate(get(templates), templateSlug); + + // Fetch service info from associated stackfile, if it exists + let services = template?.repository ? await getServices(template) : []; + + // If only 1 service, merge it with the template + if (services.length === 1) { + template = {...template, ...services[0]}; + services = []; + } else if (services.length > 1) { + // If made up from multiple services, fetch Docker info for each image + services = await Promise.all( + services.map(async (service) => { + const dockerStats = await getDockerHubStats(service.image); + return { ...service, dockerStats }; + }) + ); + } + // If image specified, fetch Docker image info from DockerHub + const dockerStats = template?.image ? await getDockerHubStats(template.image) : null; + return { template, dockerStats, services } +}; + +export const load = async ({ params }) => { + const templateSlug = params.slug as string; + if (get(templates) && get(templates).length > 0) { + return returnResults(templates, templateSlug); + } else { + const data = await fetch(templatesUrl).then((res) => res.json()); + templates.set(data.templates); + return returnResults(templates, templateSlug); } }; diff --git a/src/routes/[slug]/+page.svelte b/src/routes/[slug]/+page.svelte index 346bc5a..93aed59 100644 --- a/src/routes/[slug]/+page.svelte +++ b/src/routes/[slug]/+page.svelte @@ -1,95 +1,37 @@ -
- - -

Portainer Templates

-
- -
+
{#if template}
@@ -105,7 +47,14 @@ const services: Service[] = getServices();

{/if}
-

{template.description}

+
+

{template.description}

+ {#await template then returnedTemplate} + {#if dockerStats && dockerStats.name} + + {/if} + {/await} +
@@ -116,67 +65,44 @@ const services: Service[] = getServices();

Services

{#each returnedServices as service} -
-

{service.name}

+
+

{service.name}

+
+ {#if service.dockerStats && service.dockerStats.name} + + {/if}
+
{/each}
{/if} {/await} + + + + {#if dockerStats?.full_description} + + {:else if services.length > 0} + + {/if} + {:else} - + {/if} -