Alicia Sykes il y a 2 ans
Parent
commit
56b52ade1e
4 fichiers modifiés avec 265 ajouts et 93 suppressions
  1. 22 2
      src/Types.ts
  2. 129 0
      src/lib/ServiceStats.svelte
  3. 2 3
      src/routes/[slug]/+page.server.ts
  4. 112 88
      src/routes/[slug]/+page.svelte

+ 22 - 2
src/Types.ts

@@ -12,11 +12,15 @@ export interface Template {
   command?: string;
   interactive?: boolean;
   logo: string;
-  image: string;
+  image?: string;
   restart_policy?: 'always' | 'unless-stopped' | 'on-failure' | 'no';
   ports?: string[];
   volumes?: Volume[];
-  environment?: Environment[];
+  env?: Environment[];
+  repository?: {
+    stackfile: string;
+    url: string;
+  };
 }
 
 export interface Volume {
@@ -27,4 +31,20 @@ export interface Volume {
 export interface Environment {
   name: 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 - 0
src/lib/ServiceStats.svelte

@@ -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>

+ 2 - 3
src/routes/[slug]/+page.server.ts

@@ -10,10 +10,9 @@ export const load = async () => {
     }
   } else {
     const data = await fetch(templatesUrl).then((res) => res.json());
-    templates.set(data.templates);
-    
+    templates.set(data.templates);    
     return {
       templates: data.templates,
     }
   }
-};
+};

+ 112 - 88
src/routes/[slug]/+page.svelte

@@ -1,8 +1,11 @@
 <script lang="ts">
+  import yaml from 'js-yaml';
+
   import { page } from '$app/stores';
   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 templateSlug = $page.params.slug as string;
   
@@ -11,6 +14,70 @@
   );
 
   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>
 
 <header>
@@ -26,74 +93,38 @@
 
 {#if template}
   <section class="summary-section">
-    <h1><img src={template.logo} alt={template.title} />{template.title}</h1>
-    {#if template.categories}
+    <h1>
+      {#if template.logo} <img src={template.logo} /> {/if}
+      {template.title}
+    </h1>
+    {#if template.categories || template.category }
       <p class="tags">
-        {#each (template.categories) as tag}
+        {#each (template.categories || template.category || []) as tag}
           <span>{tag}</span>
         {/each}
       </p>
     {/if}
     <div class="content">
       <p class="description">{template.description}</p>
-      <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}</code>{/each}
-          </p>
-        </div>
-      {/if}
-      </div>
+      <ServiceStats template={template} />
     </div>
   </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}
   <TemplateNotFound />
 {/if}
@@ -156,6 +187,7 @@
       gap: 1rem;
     }
     img {
+      border-radius: 6px;
       width: 64px;
       max-height: 64px;
     }
@@ -183,34 +215,26 @@
     p.description {
       max-width: 60%;
     }
-    .stats {
-      min-width: 15rem;
-      border: 2px solid var(--background);
-      border-radius: 6px;
-      .row {
-        display: flex;
-        justify-content: space-between;
-        align-items: center;
-        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;
-        }
+  }
+
+  .service-section {
+    background: var(--card);
+    border-radius: 6px;
+    margin: 1rem;
+    padding: 1rem;
+    h2 {
+      margin: 0;
+      font-size: 2rem;
+    }
+    .service-list {
+      display: flex;
+      gap: 1rem;
+      // justify-content: space-between;
+      flex-wrap: wrap;
+      h3 {
+        margin: 0.5rem 0;
+        font-weight: 400;
       }
     }
   }
-</style>
+</style>