orchard
mirrorEvery 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
1{{define "content"}}
2{{$d := .Data}}{{$c := $d.Ctx}}
3{{template "repohead" (dict "Ctx" $c "Tab" "files")}}
4
5{{if $c.Empty}}
6 <div class="emptystate">
7 <h2>This repository is empty</h2>
8 <p>Push to it:</p>
9 <pre class="cmd">git remote add origin {{$c.CloneURL}}
10git push -u origin main</pre>
11
12 {{/*
13 An empty repository is very often the wreckage of an oversized push
14 rather than a deliberate creation: the info/refs probe succeeds and
15 creates the repository, then Cloudflare rejects the pack upload. So the
16 limit is explained here, unprompted, because this is the page somebody
17 lands on immediately after that failure.
18 */}}
19 {{if $.LoggedIn}}
20 <p class="warn">
21 <strong>If you just pushed and it failed,</strong> the most likely reason
22 is that the pack was over 100 MB. The repository exists because the
23 handshake succeeded before the upload was rejected.
24 </p>
25 {{template "pushlimit" (dict "Bridge" $c.BridgeContainer "Name" $c.Repo.Name)}}
26 {{end}}
27 </div>
28{{else}}
29 <div class="toolbar">
30 {{template "refswitcher" $d}}
31 <span class="stat">{{$d.CommitCount}} {{pluralize $d.CommitCount "commit" "commits"}}</span>
32 <a class="btn" href="/{{$c.Repo.Name}}/archive/{{$c.Rev}}.tar.gz">Download tar.gz</a>
33 </div>
34
35 <table class="tree">
36 <tbody>
37 {{range $d.Entries}}
38 <tr class="{{if .IsDir}}dir{{else}}file{{end}}">
39 <td class="icon">{{if .IsDir}}{{template "icon-dir"}}{{else if .IsSubmodule}}{{template "icon-sub"}}{{else}}{{template "icon-file"}}{{end}}</td>
40 <td class="name">
41 {{if .IsDir}}
42 <a href="/{{$c.Repo.Name}}/tree/{{$c.Rev}}/{{urlPath .Path}}">{{.Name}}/</a>
43 {{else if .IsSubmodule}}
44 <span class="submodule">{{.Name}} @ {{shortSHA .SHA}}</span>
45 {{else}}
46 <a href="/{{$c.Repo.Name}}/blob/{{$c.Rev}}/{{urlPath .Path}}">{{.Name}}</a>
47 {{end}}
48 </td>
49 <td class="size">{{if not .IsDir}}{{humanBytes .Size}}{{end}}</td>
50 </tr>
51 {{end}}
52 </tbody>
53 </table>
54
55 {{if $d.Readme}}
56 <article class="readme">
57 <div class="readmehead">{{$d.ReadmeName}}</div>
58 <div class="markdown">{{$d.Readme}}</div>
59 </article>
60 {{end}}
61
62 <section class="recent">
63 <h2>Recent commits</h2>
64 <ul class="commits">
65 {{range $d.Commits}}{{template "commitrow" (dict "Repo" $c.Repo.Name "Commit" .)}}{{end}}
66 </ul>
67 <a href="/{{$c.Repo.Name}}/log/{{$c.Rev}}">All commits</a>
68 </section>
69{{end}}
70
71{{template "clonebox" $d}}
72
73{{if and $.LoggedIn (not $c.Meta.Mirror)}}
74<details class="editbox">
75 <summary>Edit description</summary>
76 <form method="post" action="/{{$c.Repo.Name}}/edit">
77 <label>Description
78 <input type="text" name="description" value="{{$c.Meta.Description}}" maxlength="300">
79 </label>
80 <label>Topics (comma separated)
81 <input type="text" name="topics" value="{{range $i, $t := $c.Meta.Topics}}{{if $i}}, {{end}}{{$t}}{{end}}">
82 </label>
83 <label>Homepage
84 <input type="url" name="homepage" value="{{$c.Meta.Homepage}}">
85 </label>
86 <button type="submit">Save</button>
87 </form>
88</details>
89{{end}}
90{{end}}