repos
/ orchard main

orchard

mirror

Every site I host, in one repo, along with the Cloudflare Tunnel and Caddy that front them. It's all Go, Vite, and SQLite, and it runs on a desktop at home with nothing listening on an inbound port.

blogbuncaddycloudflare-tunneldockergogolanghomelabhtml-templatemonorepoself-hostedseosqlitestatic-sitetypstuptime-monitoringviteweb-analytics

6.3 KB · 141 lines · HTML Raw History
  1{{/* Default blocks. Go requires every {{template}} target to exist, so the
  2     optional ones are defined empty here and overridden by whichever page
  3     wants them: layouts are parsed before the page, and a later definition of
  4     the same name wins. This is the Go equivalent of Jinja's {% block %}
  5     defaults. */}}
  6
  7{{ define "extra_head" }}{{ end }}
  8{{ define "extra_css" }}{{ end }}
  9{{ define "extra_js" }}{{ end }}
 10{{ define "breadcrumbs" }}{{ end }}
 11
 12{{/* The bar itself is a block, not just its contents, so the home page can
 13     drop it entirely. Rendering an empty bar would leave a stray rule under
 14     the navbar with nothing in it. */}}
 15{{ define "breadcrumb_bar" }}
 16<div class="breadcrumb-bar py-2 overflow-auto d-print-none">
 17  <div class="container">
 18    {{ template "breadcrumbs" . }}
 19  </div>
 20</div>
 21{{ end }}
 22
 23{{ define "logo" }}
 24<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
 25  <polyline points="2,34 18,34 24,28 30,14 36,52 42,20 48,34 62,34"
 26    fill="none" stroke="#6b9e78" stroke-width="6"
 27    stroke-linejoin="round" stroke-linecap="round"/>
 28  <circle cx="30" cy="14" r="3.5" fill="#c9a84c"/>
 29</svg>
 30{{ end }}
 31
 32{{ define "nav" }}
 33<nav class="navbar navbar-dark navbar-expand-md d-print-none">
 34  <div class="container">
 35    <a class="navbar-brand" href="/">
 36      <span class="logo" aria-hidden="true">{{ template "logo" . }}</span>
 37      <span class="brand-text">{{ .SiteName }}</span>
 38      <span class="brand-dot" aria-hidden="true"></span>
 39    </a>
 40    <button class="navbar-toggler border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navMain" aria-controls="navMain" aria-expanded="false" aria-label="Toggle navigation">
 41      <span class="navbar-toggler-icon"></span>
 42    </button>
 43    <div class="collapse navbar-collapse" id="navMain">
 44      <ul class="navbar-nav ms-3">
 45        {{ if .Authenticated }}
 46        <li class="nav-item"><a class="nav-link {{ if eq .Path "/properties" }}active{{ end }}" href="/properties">Properties</a></li>
 47        {{ else }}
 48        <li class="nav-item"><a class="nav-link {{ if eq .Path "/" }}active{{ end }}" href="/">Home</a></li>
 49        {{ end }}
 50      </ul>
 51      <ul class="navbar-nav ms-auto">
 52        {{ if .Authenticated }}
 53        <li class="nav-item d-flex align-items-center">
 54          {{/* Signing out ends the session for every site at once, which is the
 55               only sensible meaning of it when one cookie covers all of them. */}}
 56          <a href="https://auth.bythewood.me/" class="btn btn-sm btn-outline-light">Account</a>
 57        </li>
 58        {{ else }}
 59        <li class="nav-item d-flex align-items-center">
 60          <a href="/login" class="btn btn-sm btn-outline-light">Sign in</a>
 61        </li>
 62        {{ end }}
 63      </ul>
 64    </div>
 65  </div>
 66</nav>
 67{{ end }}
 68
 69{{ define "footer" }}
 70<footer class="d-print-none">
 71  <div class="container">
 72    <div class="row links">
 73      <div class="col-12 col-lg-6 mb-4 mb-lg-0">
 74        <div class="h5 mb-3">// {{ .SiteName }}</div>
 75        <p>Self-hosted website monitoring by <a href="https://isaacbythewood.com/">{{ .AuthorName }}</a>.
 76          HTTP checks every three minutes, with Lighthouse audits, an SEO
 77          crawler, and security header analysis. Alerts go out over ntfy.</p>
 78      </div>
 79      <div class="col-6 col-lg-2">
 80        <div class="h5 mb-3">// Elsewhere</div>
 81        <ul class="list-unstyled">
 82          <li class="mb-2"><a href="https://isaacbythewood.com/" class="link-footer">Portfolio</a></li>
 83          <li class="mb-2"><a href="https://blog.bythewood.me/" class="link-footer">Blog</a></li>
 84          <li class="mb-2"><a href="https://github.com/overshard" class="link-footer" target="_blank" rel="noopener">GitHub</a></li>
 85        </ul>
 86      </div>
 87      <div class="col-6 col-lg-2">
 88        <div class="h5 mb-3">// Pages</div>
 89        <ul class="list-unstyled">
 90          <li class="mb-2"><a href="/" class="link-footer">Home</a></li>
 91          <li class="mb-2"><a href="{{ .SourceURL }}" class="link-footer" target="_blank" rel="noopener">Source</a></li>
 92        </ul>
 93      </div>
 94      <div class="col-6 col-lg-2">
 95        <div class="h5 mb-3">// Operator</div>
 96        <ul class="list-unstyled">
 97          {{ if .Authenticated }}
 98          <li class="mb-2"><a href="/properties" class="link-footer">Properties</a></li>
 99          <li class="mb-2">
100            <a href="https://auth.bythewood.me/" class="link-footer">Account</a>
101          </li>
102          {{ else }}
103          <li class="mb-2"><a href="/login" class="link-footer">Sign in</a></li>
104          {{ end }}
105        </ul>
106      </div>
107    </div>
108  </div>
109</footer>
110
111<div class="footer-bar d-print-none">
112  <div class="container">
113    <div class="row align-items-center">
114      <div class="col-sm-6 d-flex align-items-center justify-content-center justify-content-sm-start order-1 order-sm-0">
115        <small>&copy; {{ .Year }} {{ .AuthorName }} ยท Some rights reserved</small>
116      </div>
117      <div class="col-sm-6 d-flex justify-content-center justify-content-sm-end py-3 py-sm-0">
118        <a href="{{ .SourceURL }}" target="_blank" rel="noopener" class="footer-bar-link" aria-label="GitHub">
119          <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" viewBox="0 0 16 16">
120            <path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z"/>
121          </svg>
122        </a>
123      </div>
124    </div>
125  </div>
126</div>
127{{ end }}
128
129{{/* The recent-checks bar. Renders 30 empty ticks when a property has never
130     been probed, so a freshly added site shows the shape of the widget rather
131     than a gap where it will be. */}}
132{{ define "uptimeStrip" }}
133<div class="uptime-strip" aria-label="Recent checks">
134  {{ if .RecentTickStream }}
135    {{ range .RecentTickStream }}<span class="uptime-tick is-{{ . }}"></span>{{ end }}
136  {{ else }}
137    {{ range $i := seq 30 }}<span class="uptime-tick is-none"></span>{{ end }}
138  {{ end }}
139</div>
140{{ end }}