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
1package main
2
3import (
4 "encoding/json"
5 "net/http"
6 "strconv"
7
8 "chat.bythewood.me/tools"
9)
10
11// The page fetches a widget's readings here rather than being handed them with
12// the turn. That is what lets a chart change range without asking the model
13// anything, and what makes a conversation opened a week later draw today's
14// price instead of the one that was on screen when the question was asked.
15
16// Widget is the subject of a chart, recorded on the message that produced it.
17type Widget = tools.Widget
18
19func (s *site) widgetTicker(w http.ResponseWriter, r *http.Request) {
20 sym := r.URL.Query().Get("symbol")
21 if sym == "" {
22 http.Error(w, "symbol is required", 400)
23 return
24 }
25 series, err := tools.Ticker(r.Context(), s.engine.Deps(), sym, r.URL.Query().Get("range"))
26 if err != nil {
27 widgetErr(w, err)
28 return
29 }
30 writeJSON(w, series)
31}
32
33func (s *site) widgetWeather(w http.ResponseWriter, r *http.Request) {
34 q := r.URL.Query()
35 lat, err1 := strconv.ParseFloat(q.Get("lat"), 64)
36 lon, err2 := strconv.ParseFloat(q.Get("lon"), 64)
37 if err1 != nil || err2 != nil {
38 http.Error(w, "lat and lon are required", 400)
39 return
40 }
41 days, _ := strconv.Atoi(q.Get("days"))
42 rep, err := tools.Forecast(r.Context(), s.engine.Deps(), lat, lon,
43 q.Get("place"), q.Get("zip"), q.Get("country"), days)
44 if err != nil {
45 widgetErr(w, err)
46 return
47 }
48 writeJSON(w, rep)
49}
50
51// widgetErr answers with the reason rather than a bare status, because the
52// panel prints it: a rate limited host and a symbol that does not exist read
53// identically as a blank chart otherwise.
54func widgetErr(w http.ResponseWriter, err error) {
55 w.Header().Set("Content-Type", "application/json")
56 w.WriteHeader(http.StatusBadGateway)
57 _ = json.NewEncoder(w).Encode(map[string]string{"error": err.Error()})
58}