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

1.3 KB · 62 lines · Go Raw History
 1package skills
 2
 3import (
 4	"context"
 5	"encoding/json"
 6	"fmt"
 7	"io"
 8	"net/http"
 9	"strings"
10)
11
12// getJSON fetches and decodes. Every upstream here answers a block page rather
13// than JSON to a non-browser agent, so the User-Agent is not optional.
14func getJSON(ctx context.Context, d Deps, url string, out any) error {
15	req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
16	if err != nil {
17		return err
18	}
19	req.Header.Set("User-Agent", d.UA)
20	req.Header.Set("Accept", "application/json")
21
22	client := d.HTTP
23	if client == nil {
24		client = http.DefaultClient
25	}
26	resp, err := client.Do(req)
27	if err != nil {
28		return err
29	}
30	defer resp.Body.Close()
31	if resp.StatusCode != http.StatusOK {
32		return fmt.Errorf("%s: %s", url, resp.Status)
33	}
34	return json.NewDecoder(io.LimitReader(resp.Body, 4<<20)).Decode(out)
35}
36
37func containsAny(s string, subs ...string) bool {
38	for _, sub := range subs {
39		if strings.Contains(s, sub) {
40			return true
41		}
42	}
43	return false
44}
45
46func round2(v float64) float64 { return float64(int64(v*100+0.5)) / 100 }
47
48func abs(v float64) float64 {
49	if v < 0 {
50		return -v
51	}
52	return v
53}
54
55func readAll(resp *http.Response) ([]byte, error) {
56	return io.ReadAll(io.LimitReader(resp.Body, 4<<20))
57}
58
59func decode(body []byte, out any) error {
60	return json.Unmarshal(body, out)
61}