Rebuilt bauer.gg
[meta]For years bauer.gg was a single Cloudflare Worker written by hand in the dashboard’s code editor. The whole site was one fetch handler, with my resume sitting at the bottom of the file as a big object literal. The content negotiation keyed on the request’s Content-Type header rather than Accept, so a browser got the resume pretty-printed and colored, and so did curl, unless you claimed to be sending JSON. The coloring was a single regex pass over the stringified resume:
json.replace(
/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g,
(match) => {
let cls = 'number';
if (/^"/.test(match)) {
cls = /:$/.test(match) ? 'key' : 'string';
} else if (/true|false/.test(match)) {
cls = 'boolean';
} else if (/null/.test(match)) {
cls = 'null';
}
return `<span class="${cls}">${match}</span>`;
}
)
Five CSS rules finished the job: strings green, numbers darkorange, booleans blue, null magenta, keys bold. That was the entire design system, and honestly it looked pretty good. It also kept a visitor counter in Workers KV, keyed on the raw client IP with no expiry, which seemed clever at the time and reads today like a small privacy incident that never got around to happening. There was no repo. The source of truth was an editor pane in the Cloudflare dashboard.
That held up fine as a resume endpoint. It had no answer for writing, and I had a Flappy Bird port I wanted to write about.
The new site is Astro 5 with the Cloudflare adapter, still deployed as one Worker. Every route is prerendered at build time to static assets; the Worker entry point exists because the adapter emits one, not because anything renders on demand. The only client-side JavaScript on the whole site is the theme toggle.
The resume survived as the center of the thing. It lives in one typed data
file, src/data/resume.ts, that feeds both the HTML page at
/resume and the machine-readable
JSON Resume. The JSON endpoint is a single expression:
export const GET: APIRoute = () =>
new Response(JSON.stringify(resume, null, 2), { headers });
prerendered to a static file at build time. The old content negotiation is gone on purpose. Header sniffing made caching annoying and debugging worse, and two explicit URLs cost nothing.
The design is a monospace document. One family, the system ui-monospace
stack with no webfonts, 1px borders, square corners, orange used sparingly,
and a light and dark palette. The landing page opens with an ascii-art banner in a
pre tag instead of a hero image. The comment at the top of global.css
states the goal: a document, not a marketing site. Code blocks in posts get
dual-theme Shiki highlighting, github-light and github-dark-dimmed, flipped
with the site theme.
Deploys are GitHub Actions on push to main: npm ci, astro build,
npx wrangler deploy. The API token lives in repo secrets; the account id
sits in wrangler.jsonc, where it belongs, since it is not a secret. The
first deploy was a one-way door worth pausing on. Wrangler overwrites the
Worker of the same name, so the moment CI ran, the hand-edited dashboard
Worker was gone and the repo became the only source of truth. That was the
point.
The KV counter did not make the trip. Workers analytics answers the same question without storing anyone’s IP forever.
So the site is now a landing page, a blog that this post is the second entry in, the resume as both a page and JSON, and the wasm Flappy Bird port embedded at /flappy. The source is on GitHub.