From 391f483a051c9b9806e7c58c4b3ec627ed3ebd01 Mon Sep 17 00:00:00 2001
From: Alicia Sykes
Date: Sat, 22 Apr 2023 21:08:10 +0100
Subject: [PATCH] Display dynamically fetched stats
---
src/lib/ServiceStats.svelte | 148 ++++++++++-----------
src/lib/TemplateNotFound.svelte | 61 ++++++++-
src/routes/+layout.svelte | 6 +-
src/routes/[slug]/+page.server.ts | 111 ++++++++++++++--
src/routes/[slug]/+page.svelte | 205 +++++++++++-------------------
5 files changed, 302 insertions(+), 229 deletions(-)
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}
-
+
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 @@
-
+
{#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}
-