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<div class="alerts" data-alerts aria-live="polite">
3 {{range .State.Alerts}}
4 <div class="alert" data-severity="{{.Severity}}" data-urgency="{{.Urgency}}">
5 <div class="alert-head">
6 <span class="pip"></span>
7 <span class="ev">{{.Event}}</span>
8 {{if .Severity}}<span class="sev">{{.Severity}}</span>{{end}}
9 </div>
10 {{if .Headline}}<p class="hl">{{.Headline}}</p>{{end}}
11 <div class="alert-meta">
12 {{if .Area}}<span class="m"><span class="k">AREA</span><span class="v">{{.Area}}</span></span>{{end}}
13 {{if .Starts}}<span class="m"><span class="k">FROM</span><span class="v">{{.Starts}}</span></span>{{end}}
14 {{if .Until}}<span class="m"><span class="k">UNTIL</span><span class="v">{{.Until}}</span></span>{{end}}
15 {{if .UntilUnix}}<span class="m left" data-alert-left data-ends="{{.UntilUnix}}" hidden><span class="k">LEFT</span><span class="v"></span></span>{{end}}
16 {{if .Office}}<span class="m"><span class="k">NWS</span><span class="v">{{.Office}}</span></span>{{end}}
17 </div>
18 </div>
19 {{end}}
20</div>
21
22<section class="panel markets" data-panel="markets">
23 <div class="bracket tl"></div><div class="bracket tr"></div>
24 <div class="bracket bl"></div><div class="bracket br"></div>
25
26 <header class="panel-head">
27 <span class="idx">01</span>
28 <h2>MARKETS</h2>
29 <span class="rule"></span>
30 <span class="session" data-session="{{.State.Market.Session}}">
31 <span class="pip"></span>
32 <span class="badge" data-market-session>{{.State.Market.Session}}</span>
33 <span class="phase" data-market-phase>{{.State.Market.Phase}}</span>
34 </span>
35 <span class="drawdown">
36 <span class="k">SPX / 52W HIGH</span>
37 <span class="v" data-market-drawdown>{{if .State.Market.Drawdown}}{{.State.Market.Drawdown}}{{else}}—{{end}}</span>
38 </span>
39 </header>
40
41 <div class="cards" data-market-cards>
42 {{range .State.Market.Cards}}{{template "card" .}}{{end}}
43 </div>
44</section>
45
46<div class="band market-band">
47
48 <section class="panel rates" data-panel="rates">
49 <div class="bracket tl"></div><div class="bracket tr"></div>
50 <div class="bracket bl"></div><div class="bracket br"></div>
51 <header class="panel-head">
52 <span class="idx">02</span>
53 <h2>RATES</h2>
54 <span class="rule"></span>
55 <span class="panel-meta">TREASURY</span>
56 </header>
57 <ul class="rate-rows" data-rates>
58 {{range .State.Rates.Rows}}
59 <li data-dir="{{.Direction}}">
60 <span class="k">{{.Label}}</span>
61 <span class="v">{{if .Unavailable}}—{{else}}{{.Yield}}{{end}}</span>
62 <span class="scale"><i style="width:{{.Fill}}%"></i></span>
63 <span class="d">{{.Change}}</span>
64 </li>
65 {{end}}
66 </ul>
67 <p class="curve" data-curve-state="{{.State.Rates.CurveState}}">
68 <span class="k">10Y − 3M</span>
69 <span class="v" data-curve>{{if .State.Rates.Curve}}{{.State.Rates.Curve}}{{else}}—{{end}}</span>
70 <span class="shape" data-curve-shape>{{.State.Rates.Shape}}</span>
71 </p>
72 </section>
73
74
75 <section class="panel sectors" data-panel="sectors">
76 <div class="bracket tl"></div><div class="bracket tr"></div>
77 <div class="bracket bl"></div><div class="bracket br"></div>
78 <header class="panel-head">
79 <span class="idx">03</span>
80 <h2>SECTORS</h2>
81 <span class="rule"></span>
82 <span class="panel-meta">BEST TO WORST</span>
83 </header>
84 <div class="heat" data-sectors>
85 {{range .State.Sectors}}
86 <div class="cell" data-dir="{{.Direction}}" data-heat="{{.Heat}}"{{if .Benchmark}} data-benchmark="yes"{{end}}>
87 <span class="s">{{.Label}}</span>
88 <span class="p">{{if .Unavailable}}—{{else}}{{.Percent}}{{end}}</span>
89 </div>
90 {{end}}
91 </div>
92 </section>
93
94 <section class="panel conditions" data-panel="conditions">
95 <div class="bracket tl"></div><div class="bracket tr"></div>
96 <div class="bracket bl"></div><div class="bracket br"></div>
97 <header class="panel-head">
98 <span class="idx">04</span>
99 <h2>CONDITIONS</h2>
100 <span class="rule"></span>
101 <span class="panel-meta">S&P 500</span>
102 </header>
103 <div data-signal>
104 <p class="verdict" data-level="{{.State.Signal.Level}}" data-signal-headline>{{.State.Signal.Headline}}</p>
105 <ul class="conditions" data-conditions>
106 {{range .State.Signal.Conditions}}{{template "condition" .}}{{end}}
107 </ul>
108 </div>
109 <p class="footnote">A READOUT, NOT A FORECAST. NOBODY CALLS THE BOTTOM.</p>
110 </section>
111
112
113 <section class="panel earnings" data-panel="earnings">
114 <div class="bracket tl"></div><div class="bracket tr"></div>
115 <div class="bracket bl"></div><div class="bracket br"></div>
116 <header class="panel-head">
117 <span class="idx">05</span>
118 <h2>EARNINGS</h2>
119 <span class="rule"></span>
120 <span class="panel-meta">TOP 100 OF THE S&P 500</span>
121 </header>
122 <ul class="earning-rows" data-earnings>
123 {{range .State.Earnings.Reported}}{{template "earning-reported" .}}{{end}}
124 {{range .State.Earnings.Upcoming}}{{template "earning-upcoming" .}}{{end}}
125 {{if and (not .State.Earnings.Reported) (not .State.Earnings.Upcoming)}}<li class="empty">NOTHING MAJOR SCHEDULED</li>{{end}}
126 </ul>
127 </section>
128
129</div>
130
131<div class="band local-band">
132 <section class="panel weather" data-panel="weather">
133 <div class="bracket tl"></div><div class="bracket tr"></div>
134 <div class="bracket bl"></div><div class="bracket br"></div>
135 <header class="panel-head">
136 <span class="idx">06</span>
137 <h2>ATMOS</h2>
138 <span class="rule"></span>
139 <span class="panel-meta">{{.State.Weather.Place}}</span>
140 </header>
141 <div class="weather-body" data-weather>
142 {{if .State.Weather.Unavailable}}
143 <p class="dim">NO SIGNAL</p>
144 {{else}}
145 <div class="temp-row">
146 <span class="temp">{{.State.Weather.Temperature}}<span class="deg">°</span></span>
147 <span class="cond">
148 <span class="cond-label">{{.State.Weather.Condition}}</span>
149 <span class="cond-feels">FEELS {{.State.Weather.Feels}}°</span>
150 </span>
151 <span class="range">
152 <span class="hi">{{.State.Weather.High}}°</span>
153 <span class="lo">{{.State.Weather.Low}}°</span>
154 </span>
155 </div>
156 <div class="now-row">
157 <span><b>RAIN</b> {{.State.Weather.Rain}}</span>
158 <span><b>WIND</b> {{.State.Weather.Wind}}</span>
159 </div>
160 {{end}}
161 </div>
162
163 {{/*
164 Siblings of data-weather, not children. Each is patched by its own
165 renderer on its own poll, and nesting them meant the weather frame
166 replaced them out of existence on the first update.
167 */}}
168 <div class="gauges" data-air>
169 {{template "gauge" dict "Key" "UV" "Value" .State.Weather.UV "Band" .State.Weather.UVState "Fill" .State.Weather.UVFill "Level" .State.Weather.UVLevel "Known" true}}
170 {{template "gauge" dict "Key" "AQI" "Value" .State.Air.AQI "Band" .State.Air.AQIState "Fill" .State.Air.AQIFill "Level" .State.Air.AQILevel "Known" .State.Air.Known}}
171 {{template "gauge" dict "Key" "POLLEN" "Value" .State.Air.Pollen "Band" .State.Air.PollenState "Fill" .State.Air.PollenFill "Level" .State.Air.PollenLevel "Known" .State.Air.PollenKnown}}
172 </div>
173
174 <div class="hours" data-hours>
175 <div class="hours-head">
176 <span class="k">NEXT {{len .State.Weather.Hours}} HOURS</span>
177 <span class="v">CHANCE OF RAIN</span>
178 </div>
179 <div class="hour-plot">
180 {{range .State.Weather.Hours}}
181 <span class="hbar" data-wet="{{if ge .Rain 50}}yes{{else}}no{{end}}"><i style="height:{{.Rain}}%"></i></span>
182 {{end}}
183 </div>
184 <div class="hour-cols">
185 {{range .State.Weather.Hours}}{{template "hour" .}}{{end}}
186 </div>
187 </div>
188
189 <div class="daylight" data-daylight>
190 <span class="t">{{.State.Weather.Sunrise}}</span>
191 <span class="track"><i style="width:{{.State.Weather.DayPercent}}%"></i></span>
192 <span class="t">{{.State.Weather.Sunset}}</span>
193 </div>
194 </section>
195
196
197 <section class="panel outdoors" data-panel="outdoors">
198 <div class="bracket tl"></div><div class="bracket tr"></div>
199 <div class="bracket bl"></div><div class="bracket br"></div>
200 <header class="panel-head">
201 <span class="idx">07</span>
202 <h2>OUT</h2>
203 <span class="rule"></span>
204 <span class="panel-meta">WEEKEND</span>
205 </header>
206 <ul class="outlook" data-outlook>
207 {{range .State.Outlook.Days}}{{template "outlook" .}}{{else}}<li class="empty">AWAITING FORECAST</li>{{end}}
208 </ul>
209 </section>
210
211
212 <section class="panel steam" data-panel="steam">
213 <div class="bracket tl"></div><div class="bracket tr"></div>
214 <div class="bracket bl"></div><div class="bracket br"></div>
215 <header class="panel-head">
216 <span class="idx">08</span>
217 <h2>STEAM</h2>
218 <span class="rule"></span>
219 <span class="panel-meta">TOP SELLERS</span>
220 </header>
221 <ol class="games" data-steam>
222 {{range .State.Steam}}{{template "game" .}}{{else}}<li class="empty">AWAITING STORE</li>{{end}}
223 </ol>
224 </section>
225
226 <section class="panel streaming" data-panel="streaming">
227 <div class="bracket tl"></div><div class="bracket tr"></div>
228 <div class="bracket bl"></div><div class="bracket br"></div>
229 <header class="panel-head">
230 <span class="idx">09</span>
231 <h2>STREAMING</h2>
232 <span class="rule"></span>
233 <span class="panel-meta">TRENDING</span>
234 </header>
235 <ol class="titles" data-streaming>
236 {{range .State.Streaming}}{{template "title" .}}{{else}}<li class="empty">AWAITING LISTINGS</li>{{end}}
237 </ol>
238
239 </section>
240
241
242 <section class="panel systems" data-panel="systems">
243 <div class="bracket tl"></div><div class="bracket tr"></div>
244 <div class="bracket bl"></div><div class="bracket br"></div>
245 <header class="panel-head">
246 <span class="idx">10</span>
247 <h2>SYSTEMS</h2>
248 <span class="rule"></span>
249 <span class="panel-meta" data-systems-summary>{{.State.Systems.Up}}/{{.State.Systems.Total}} UP</span>
250 </header>
251 <ul class="rows" data-systems>
252 {{range .State.Systems.Rows}}
253 <li data-state="{{.State}}">
254 <span class="pip"></span>
255 {{if .URL}}<a class="name" href="{{.URL}}" rel="noopener noreferrer" target="_blank">{{.Label}}</a>{{else}}<span class="name">{{.Label}}</span>{{end}}
256 <span class="traffic" data-level="{{.Level}}" data-trend="{{.Trend}}" title="{{.Requests}} requests">
257 <i></i><i></i><i></i><i></i>
258 </span>
259 <span class="bar"></span>
260 <span class="latency" title="95th percentile server handling time over 24h, network excluded">{{if .Response}}{{.Response}}{{else}}—{{end}}</span>
261 <span class="errors" data-any="{{if gt .Errors 0}}yes{{else}}no{{end}}">{{if .KnowError}}{{.Errors}}E{{else}}—{{end}}</span>
262 </li>
263 {{end}}
264 </ul>
265 <p class="footnote" data-systems-note>{{if .State.Systems.Window}}{{.State.Systems.Requests}} REQ / {{.State.Systems.Errors}} ERR / P95 / LAST {{.State.Systems.Window}}H{{end}}</p>
266 </section>
267
268
269 <section class="panel uplink" data-panel="uplink">
270 <div class="bracket tl"></div><div class="bracket tr"></div>
271 <div class="bracket bl"></div><div class="bracket br"></div>
272 <header class="panel-head">
273 <span class="idx">11</span>
274 <h2>UPLINK</h2>
275 <span class="rule"></span>
276 <span class="panel-meta">UPSTREAM</span>
277 </header>
278 <ul class="rows" data-feeds>
279 {{range .State.Feeds}}
280 <li data-state="{{.State}}">
281 <span class="pip"></span>
282 <span class="name">{{.Name}}</span>
283 <span class="bar"></span>
284 <span class="age">{{.Age}}</span>
285 <span class="load" data-hot="{{if ge .Load 50}}yes{{else}}no{{end}}">{{.Used}}/{{.Budget}}</span>
286 </li>
287 {{end}}
288 </ul>
289 <p class="footnote">CALLS THIS HOUR / HOURLY CEILING</p>
290 </section>
291
292</div>
293
294<div class="band news-band">
295 <section class="panel wire" data-panel="wire">
296 <div class="bracket tl"></div><div class="bracket tr"></div>
297 <div class="bracket bl"></div><div class="bracket br"></div>
298 <header class="panel-head">
299 <span class="idx">12</span>
300 <h2>WIRE</h2>
301 <span class="rule"></span>
302 <span class="panel-meta">NPR / BBC</span>
303 </header>
304 <ol class="headlines" data-wire>
305 {{range .State.Wire}}{{template "headline" .}}{{else}}<li class="empty">AWAITING WIRE</li>{{end}}
306 </ol>
307 </section>
308
309 <section class="panel feed" data-panel="hn">
310 <div class="bracket tl"></div><div class="bracket tr"></div>
311 <div class="bracket bl"></div><div class="bracket br"></div>
312 <header class="panel-head">
313 <span class="idx">13</span>
314 <h2>HACKER NEWS</h2>
315 <span class="rule"></span>
316 <a class="panel-meta" href="https://news.ycombinator.com" rel="noopener noreferrer" target="_blank">NEWS.YCOMBINATOR.COM</a>
317 </header>
318 <div data-hn>{{template "stories" .State.HN}}</div>
319 </section>
320
321 <section class="panel feed" data-panel="lobsters">
322 <div class="bracket tl"></div><div class="bracket tr"></div>
323 <div class="bracket bl"></div><div class="bracket br"></div>
324 <header class="panel-head">
325 <span class="idx">14</span>
326 <h2>LOBSTERS</h2>
327 <span class="rule"></span>
328 <a class="panel-meta" href="https://lobste.rs" rel="noopener noreferrer" target="_blank">LOBSTE.RS</a>
329 </header>
330 <div data-lobsters>{{template "stories" .State.Lobsters}}</div>
331 </section>
332
333</div>
334{{end}}