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.PHONY: run build frontend maps seed clean
2
3# Vite watches and rebuilds build/dist while Go serves it from disk. Templates are
4# embedded, so a template change needs a Go restart; CSS and JS changes only
5# need a browser reload.
6#
7# Go waits for the manifest rather than sleeping a guessed number of seconds.
8# The server treats a missing manifest as fatal, so the startup order matters,
9# and this site's first Vite build is the slowest in the repo: Bootstrap SCSS
10# plus Chart.js, d3 and topojson.
11run:
12 cd frontend && bun run dev & \
13 i=0; while [ ! -f build/dist/.vite/manifest.json ]; do \
14 i=$$((i+1)); \
15 if [ $$i -gt 120 ]; then echo "vite did not produce a manifest in 60s" >&2; exit 1; fi; \
16 sleep 0.5; \
17 done; \
18 go run .
19
20frontend:
21 cd frontend && bun install --frozen-lockfile && bun run build
22
23# Per-country admin-1 topojson from Natural Earth, about 10MB of output. Its own
24# target rather than part of `build`, because the input is a fixed dataset and
25# regenerating it every time would add a minute for nothing. The Dockerfile runs
26# it in its own layer.
27maps:
28 cd frontend && bun run build:maps
29
30# The release binary. -tags embed swaps assets_disk.go for assets_embed.go, so
31# the Vite bundle and the topojson are compiled into the executable. Both
32# prerequisites are requirements rather than ordering: the embed directives read
33# build/ at compile time and fail if it is not there.
34build: frontend build/static_maps
35 go build -tags embed -trimpath -ldflags="-s -w" -o ../../bin/analytics.bythewood.me .
36
37# A directory target, so this runs once and then never again. A release build
38# cannot proceed without the maps, but the input is a fixed dataset, so
39# existence is the right check rather than a rebuild.
40build/static_maps:
41 $(MAKE) maps
42
43# Realistic fake traffic in a "Seed Test" property, so the dashboard can be
44# looked at without waiting for a real site to accumulate months of events.
45# Override with `make seed SESSIONS=2000 DAYS=90`.
46SESSIONS ?= 500
47DAYS ?= 60
48seed:
49 go run . -seed -seed-sessions $(SESSIONS) -seed-days $(DAYS)
50
51clean:
52 rm -rf build frontend/node_modules