Luigit
repositories / bugabinga.net

bugabinga.net

personal infrastructure for bugabinga!

owned by admin

services/toad/internal/api/web/dashboard.html

Raw
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Toad deployment control</title>
  <link rel="stylesheet" href="/assets/dashboard.css">
  <link rel="stylesheet" href="/assets/theme.css">
  <script defer src="/assets/dashboard.js"></script>
</head>
<body>
<main class="shell">
  <header class="top">
    <div class="identity">
      <svg class="mark" viewBox="0 0 52 52" role="img" aria-label="Toad">
        <path d="M6 25C6 11 15 4 26 4s20 7 20 21c-5 3-10 4-20 4S11 28 6 25z" fill="var(--nugu-ui-accent)"/>
        <circle cx="26" cy="12" r="6" fill="var(--nugu-content-backdrop)"/><circle cx="12" cy="20" r="4" fill="var(--nugu-content-backdrop)"/><circle cx="40" cy="20" r="4" fill="var(--nugu-content-backdrop)"/>
        <path d="M15 27c0 13 4 20 11 20s11-7 11-20c-3 1-7 2-11 2s-8-1-11-2z" fill="var(--nugu-content-normal)"/>
        <circle cx="22" cy="36" r="2" fill="var(--nugu-content-backdrop)"/><circle cx="30" cy="36" r="2" fill="var(--nugu-content-backdrop)"/>
      </svg>
      <div class="brand">toad</div>
    </div>
    {{if .Services}}<div class="fleet {{if eq .Healthy (len .Services)}}healthy{{else}}unhealthy{{end}}"><span class="nf">󰗶</span> {{.Healthy}}/{{len .Services}} healthy</div>{{end}}
  </header>

  {{if .Services}}
  <nav class="grid" aria-label="Services">
    {{range .Services}}
    <a class="card {{if $.Selected}}{{if eq .Service.Name $.Selected.Service.Name}}selected{{end}}{{end}}" href="/?service={{.Service.Name}}">
      <span class="title"><strong>{{.Service.Name}}</strong><span class="{{.Health}} nf" title="{{.Health}}">󰗶</span></span>
      <span class="digest">{{shortDigest .State.Current}}</span>
      <span class="meta"><span>generation {{.State.Generation}}</span><span>{{elapsed .State.UpdatedAt}}</span></span>
    </a>
    {{end}}
  </nav>
  {{else}}
  <section class="empty"><h1>No services enrolled</h1></section>
  {{end}}

  {{with .Selected}}
  <article class="panel">
    <header class="panel-head">
      <div><h1>{{.Service.Name}}</h1><span class="repository">{{.Service.ImageRepository}}</span></div>
      <span class="{{.Health}}"><span class="nf">󰗶</span> {{.Health}}</span>
    </header>
    <nav class="tabs" role="tablist" aria-label="Service detail">
      <button class="tab" role="tab" aria-selected="true" aria-controls="overview">Overview</button>
      <button class="tab" role="tab" aria-selected="false" aria-controls="configuration">Configuration</button>
      <button class="tab" role="tab" aria-selected="false" aria-controls="systemd">Systemd</button>
      <button class="tab" role="tab" aria-selected="false" aria-controls="operations">Operations</button>
    </nav>

    <section id="overview" role="tabpanel">
      <div class="facts">
        <section class="group"><h2>Deployment</h2>
          <div class="fact"><span>Generation</span><code>{{.State.Generation}}</code></div>
          <div class="fact"><span>Current</span><code>{{shortDigest .State.Current}}</code></div>
          <div class="fact"><span>Rollback</span><code>{{shortDigest .State.Rollback}}</code></div>
        </section>
        <section class="group"><h2>Container</h2>
          <div class="fact"><span>Running</span><code class="{{if .Container.Running}}healthy{{else}}unhealthy{{end}}">{{.Container.Running}}</code></div>
          <div class="fact"><span>Digest</span><code>{{shorten .Container.ImageDigest}}</code></div>
          <div class="fact"><span>Restarts</span><code>{{.Unit.Restarts}}</code></div>
        </section>
        <section class="group"><h2>Systemd</h2>
          <div class="fact"><span>Unit</span><code>{{.Service.UnitName}}</code></div>
          <div class="fact"><span>Active</span><code>{{.Unit.ActiveState}}</code></div>
          <div class="fact"><span>Result</span><code>{{.Unit.Result}}</code></div>
        </section>
      </div>
    </section>

    <section id="configuration" role="tabpanel" hidden>
      <div class="rows">
        <div class="row"><span class="label">Image repository</span><code>{{.Service.ImageRepository}}</code><button class="copy nf" data-copy="{{.Service.ImageRepository}}" aria-label="Copy image repository" title="Copy">󰆏</button></div>
        <div class="row"><span class="label">Container</span><code>{{.Service.ContainerName}}</code><button class="copy nf" data-copy="{{.Service.ContainerName}}" aria-label="Copy container name" title="Copy">󰆏</button></div>
        <div class="row"><span class="label">Readiness status</span><code class="{{.Health}}">{{.Health}}</code><span></span></div>
        <div class="row"><span class="label">Probation</span><code>{{probation .Service}}</code><span></span></div>
      </div>
      <p class="boundary">Raw Quadlets and environment values are excluded because they may contain secret references or host internals.</p>
    </section>

    <section id="systemd" role="tabpanel" hidden>
      <div class="rows">
        <div class="row"><span class="label">Unit</span><code>{{.Service.UnitName}}</code><button class="copy nf" data-copy="{{.Service.UnitName}}" aria-label="Copy unit name" title="Copy">󰆏</button></div>
        <div class="row"><span class="label">Load state</span><code>{{.Unit.LoadState}}</code><span></span></div>
        <div class="row"><span class="label">Active state</span><code>{{.Unit.ActiveState}}</code><span></span></div>
        <div class="row"><span class="label">Result</span><code>{{.Unit.Result}}</code><span></span></div>
        <div class="row"><span class="label">NRestarts</span><code>{{.Unit.Restarts}}</code><span></span></div>
      </div>
      <p class="boundary">Toad reads its own user manager over D-Bus. It does not mount or expose the host journal.</p>
    </section>

    <section id="operations" role="tabpanel" hidden>
      {{range .Operations}}
      <details class="operation">
        <summary><strong class="{{.Operation.Phase}}">{{.Operation.Phase}}</strong><span>generation {{.Operation.Generation}}{{with .Operation.Reason}} · {{.}}{{end}}</span><time>{{elapsed .Operation.UpdatedAt}}</time></summary>
        {{if .Log}}<div class="log-tools"><button class="copy nf" data-copy-target="log-{{.Operation.ID}}" aria-label="Copy operation log" title="Copy log">󰆏</button></div><pre class="log" id="log-{{.Operation.ID}}">{{.Log}}</pre>{{end}}
      </details>
      {{else}}<p class="empty">No operations recorded</p>{{end}}
    </section>
  </article>
  {{end}}
</main>
</body>
</html>