Services
This commit is contained in:
24
src/Types.ts
24
src/Types.ts
@@ -12,11 +12,15 @@ export interface Template {
|
|||||||
command?: string;
|
command?: string;
|
||||||
interactive?: boolean;
|
interactive?: boolean;
|
||||||
logo: string;
|
logo: string;
|
||||||
image: string;
|
image?: string;
|
||||||
restart_policy?: 'always' | 'unless-stopped' | 'on-failure' | 'no';
|
restart_policy?: 'always' | 'unless-stopped' | 'on-failure' | 'no';
|
||||||
ports?: string[];
|
ports?: string[];
|
||||||
volumes?: Volume[];
|
volumes?: Volume[];
|
||||||
environment?: Environment[];
|
env?: Environment[];
|
||||||
|
repository?: {
|
||||||
|
stackfile: string;
|
||||||
|
url: string;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Volume {
|
export interface Volume {
|
||||||
@@ -27,4 +31,20 @@ export interface Volume {
|
|||||||
export interface Environment {
|
export interface Environment {
|
||||||
name: string;
|
name: string;
|
||||||
label?: string;
|
label?: string;
|
||||||
|
set?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Service {
|
||||||
|
name: string;
|
||||||
|
image?: string;
|
||||||
|
entrypoint?: string;
|
||||||
|
restart_policy?: 'always' | 'unless-stopped' | 'on-failure' | 'no';
|
||||||
|
volumes?: Volume[];
|
||||||
|
command?: string;
|
||||||
|
ports?: string[];
|
||||||
|
build?: string;
|
||||||
|
interactive?: boolean;
|
||||||
|
environment?: Environment[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TemplateOrService extends Template, Service {}
|
||||||
|
|||||||
129
src/lib/ServiceStats.svelte
Normal file
129
src/lib/ServiceStats.svelte
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { TemplateOrService } from '$src/Types';
|
||||||
|
|
||||||
|
export let template: TemplateOrService;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="stats">
|
||||||
|
{#if template.type}
|
||||||
|
<div class="row">
|
||||||
|
<span class="lbl">Type</span>
|
||||||
|
{#if template.type === 1}
|
||||||
|
<span>Container</span>
|
||||||
|
{:else if template.type === 2}
|
||||||
|
<span>Swarm</span>
|
||||||
|
{:else if template.type === 3}
|
||||||
|
<span>Kubernetes</span>
|
||||||
|
{:else}
|
||||||
|
<span>Unknown</span>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{#if template.platform}
|
||||||
|
<div class="row">
|
||||||
|
<span class="lbl">Platform</span>
|
||||||
|
<code>{template.platform}</code>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{#if template.image}
|
||||||
|
<div class="row">
|
||||||
|
<span class="lbl">Image</span>
|
||||||
|
<code>{template.image}</code>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{#if template.command}
|
||||||
|
<div class="row">
|
||||||
|
<span class="lbl">Command</span>
|
||||||
|
<code>{template.command}</code>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{#if typeof template.interactive === 'boolean'}
|
||||||
|
<div class="row">
|
||||||
|
<span class="lbl">Interactive</span>
|
||||||
|
<code>{template.interactive ? 'Yes' : 'No'}</code>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{#if template.ports}
|
||||||
|
<div class="row">
|
||||||
|
<span class="lbl">Ports</span>
|
||||||
|
<p>
|
||||||
|
{#each template.ports as port}<code>{port}</code>{/each}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{#if template.volumes}
|
||||||
|
<div class="row">
|
||||||
|
<span class="lbl">Volumes</span>
|
||||||
|
<p>
|
||||||
|
{#each template.volumes as volume}<code>{volume.container || volume}</code>{/each}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{#if template.restart_policy}
|
||||||
|
<div class="row">
|
||||||
|
<span class="lbl">Restart Policy</span>
|
||||||
|
<code>{template.restart_policy}</code>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{#if template.repository}
|
||||||
|
<div class="row">
|
||||||
|
<span class="lbl">Sourced</span>
|
||||||
|
<a href={template.repository.url}>Repo</a>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{#if template.entrypoint}
|
||||||
|
<div class="row">
|
||||||
|
<span class="lbl">Entrypoint</span>
|
||||||
|
<code>{template.entrypoint}</code>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{#if template.build}
|
||||||
|
<div class="row">
|
||||||
|
<span class="lbl">Build</span>
|
||||||
|
<code>{template.build}</code>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{#if template.env}
|
||||||
|
<div class="row">
|
||||||
|
<span class="lbl">Env Vars</span>
|
||||||
|
<p>
|
||||||
|
{#each template.env as env}<code>{env.name}={env.set || env.value || env.default}</code>{/each}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.stats {
|
||||||
|
min-width: 15rem;
|
||||||
|
border: 2px solid var(--background);
|
||||||
|
border-radius: 6px;
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
padding: 0.5rem;
|
||||||
|
gap: 0.5rem;
|
||||||
|
&:not(:last-child) {
|
||||||
|
border-bottom: 2px dotted var(--background);
|
||||||
|
}
|
||||||
|
span {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.lbl {
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
min-width: 5rem;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -11,7 +11,6 @@ export const load = async () => {
|
|||||||
} else {
|
} else {
|
||||||
const data = await fetch(templatesUrl).then((res) => res.json());
|
const data = await fetch(templatesUrl).then((res) => res.json());
|
||||||
templates.set(data.templates);
|
templates.set(data.templates);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
templates: data.templates,
|
templates: data.templates,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import yaml from 'js-yaml';
|
||||||
|
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
import TemplateNotFound from '$lib/TemplateNotFound.svelte';
|
import TemplateNotFound from '$lib/TemplateNotFound.svelte';
|
||||||
import type { Template } from '$src/Types';
|
import type { Template, Service } from '$src/Types';
|
||||||
|
|
||||||
|
import ServiceStats from '$lib/ServiceStats.svelte';
|
||||||
const templates = $page.data.templates as Template[];
|
const templates = $page.data.templates as Template[];
|
||||||
const templateSlug = $page.params.slug as string;
|
const templateSlug = $page.params.slug as string;
|
||||||
|
|
||||||
@@ -11,6 +14,70 @@
|
|||||||
);
|
);
|
||||||
|
|
||||||
console.log(template);
|
console.log(template);
|
||||||
|
|
||||||
|
|
||||||
|
type Service = {
|
||||||
|
name: string;
|
||||||
|
image: string;
|
||||||
|
entrypoint: string;
|
||||||
|
command: string;
|
||||||
|
ports: string[];
|
||||||
|
build: string;
|
||||||
|
interactive: boolean;
|
||||||
|
volumes: { bind: string; container: string }[];
|
||||||
|
restart_policy: string;
|
||||||
|
environment: { name: string; value: string }[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const getServices = async (): Promise<Service[]> => {
|
||||||
|
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 [];
|
||||||
|
|
||||||
|
console.log(parsedData);
|
||||||
|
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],
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
console.log(someServices);
|
||||||
|
return someServices;
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching or parsing YAML:', error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const services: Service[] = getServices();
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<header>
|
<header>
|
||||||
@@ -26,74 +93,38 @@
|
|||||||
|
|
||||||
{#if template}
|
{#if template}
|
||||||
<section class="summary-section">
|
<section class="summary-section">
|
||||||
<h1><img src={template.logo} alt={template.title} />{template.title}</h1>
|
<h1>
|
||||||
{#if template.categories}
|
{#if template.logo} <img src={template.logo} /> {/if}
|
||||||
|
{template.title}
|
||||||
|
</h1>
|
||||||
|
{#if template.categories || template.category }
|
||||||
<p class="tags">
|
<p class="tags">
|
||||||
{#each (template.categories) as tag}
|
{#each (template.categories || template.category || []) as tag}
|
||||||
<span>{tag}</span>
|
<span>{tag}</span>
|
||||||
{/each}
|
{/each}
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<p class="description">{template.description}</p>
|
<p class="description">{template.description}</p>
|
||||||
<div class="stats">
|
<ServiceStats template={template} />
|
||||||
{#if template.type}
|
|
||||||
<div class="row">
|
|
||||||
<span class="lbl">Type</span>
|
|
||||||
{#if template.type === 1}
|
|
||||||
<span>Container</span>
|
|
||||||
{:else if template.type === 2}
|
|
||||||
<span>Swarm</span>
|
|
||||||
{:else if template.type === 3}
|
|
||||||
<span>Kubernetes</span>
|
|
||||||
{:else}
|
|
||||||
<span>Unknown</span>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
{#if template.platform}
|
|
||||||
<div class="row">
|
|
||||||
<span class="lbl">Platform</span>
|
|
||||||
<code>{template.platform}</code>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
{#if template.image}
|
|
||||||
<div class="row">
|
|
||||||
<span class="lbl">Image</span>
|
|
||||||
<code>{template.image}</code>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
{#if template.command}
|
|
||||||
<div class="row">
|
|
||||||
<span class="lbl">Command</span>
|
|
||||||
<code>{template.command}</code>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
{#if typeof template.interactive === 'boolean'}
|
|
||||||
<div class="row">
|
|
||||||
<span class="lbl">Interactive</span>
|
|
||||||
<code>{template.interactive ? 'Yes' : 'No'}</code>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
{#if template.ports}
|
|
||||||
<div class="row">
|
|
||||||
<span class="lbl">Ports</span>
|
|
||||||
<p>
|
|
||||||
{#each template.ports as port}<code>{port}</code>{/each}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
{#if template.volumes}
|
|
||||||
<div class="row">
|
|
||||||
<span class="lbl">Volumes</span>
|
|
||||||
<p>
|
|
||||||
{#each template.volumes as volume}<code>{volume.container}</code>{/each}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
{#await services then returnedServices}
|
||||||
|
{#if returnedServices && returnedServices.length > 0}
|
||||||
|
<section class="service-section">
|
||||||
|
<h2>Services</h2>
|
||||||
|
<div class="service-list">
|
||||||
|
{#each returnedServices as service}
|
||||||
|
<div>
|
||||||
|
<h3>{service.name}</h3>
|
||||||
|
<ServiceStats template={service} />
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{/if}
|
||||||
|
{/await}
|
||||||
{:else}
|
{:else}
|
||||||
<TemplateNotFound />
|
<TemplateNotFound />
|
||||||
{/if}
|
{/if}
|
||||||
@@ -156,6 +187,7 @@
|
|||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
}
|
}
|
||||||
img {
|
img {
|
||||||
|
border-radius: 6px;
|
||||||
width: 64px;
|
width: 64px;
|
||||||
max-height: 64px;
|
max-height: 64px;
|
||||||
}
|
}
|
||||||
@@ -183,33 +215,25 @@
|
|||||||
p.description {
|
p.description {
|
||||||
max-width: 60%;
|
max-width: 60%;
|
||||||
}
|
}
|
||||||
.stats {
|
}
|
||||||
min-width: 15rem;
|
|
||||||
border: 2px solid var(--background);
|
.service-section {
|
||||||
border-radius: 6px;
|
background: var(--card);
|
||||||
.row {
|
border-radius: 6px;
|
||||||
display: flex;
|
margin: 1rem;
|
||||||
justify-content: space-between;
|
padding: 1rem;
|
||||||
align-items: center;
|
h2 {
|
||||||
flex-wrap: wrap;
|
margin: 0;
|
||||||
padding: 0.5rem;
|
font-size: 2rem;
|
||||||
gap: 0.5rem;
|
}
|
||||||
&:not(:last-child) {
|
.service-list {
|
||||||
border-bottom: 2px dotted var(--background);
|
display: flex;
|
||||||
}
|
gap: 1rem;
|
||||||
span {
|
// justify-content: space-between;
|
||||||
font-style: italic;
|
flex-wrap: wrap;
|
||||||
}
|
h3 {
|
||||||
p {
|
margin: 0.5rem 0;
|
||||||
margin: 0;
|
font-weight: 400;
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
.lbl {
|
|
||||||
font-weight: 400;
|
|
||||||
font-style: normal;
|
|
||||||
min-width: 5rem;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user