docs(project-state): 2026-10-17 re-check — E4/E5 closed, og-default rewritten, the held churn post, the nginx 301 scheme fix, GitHub mirror re-synced
Deploy / build (push) Successful in 43s

This commit is contained in:
hoelee committed 2026-10-17 14:36:58 +08:00
1 parent f4b2ce9658
commit 18fa79fa43
1 file changed
+66 -7
+66 -7
View File
@@ -133,7 +133,8 @@ The user asked for the dashboard to be reachable remotely **without** publishing
| Rollback | Start the gate again (`sudo /usr/local/bin/docker start umami-gateway`, or start stack 285 in Portainer) → re-point the vhost + `ReverseProxy.json` to `5410` → reload nginx. Backups of both files sit next to the originals. (The gate container is now **`Exited (0)`** — it was stopped once the outpost took over, so port 5410 is free; the stack is kept as the rollback asset) |
| Note | The `hermes3` authentik API token expired mid-session (DB says `expires 2026-09-28 22:17:08Z`); the leftover self-test account was removed through authentik's own ORM (`ak shell`), so no admin-group test user is left behind |
**Step E2 — Wire the two sites together (www.hoelee.com → blog).** ⏸ Deferred by user 2026-09-29
**Step E2 — Wire the two sites together (www.hoelee.com → blog).** ⏸ Recorded and deferred — user decision 2026-10-17:
*先记录 www.hoelee.com 过后再改* (record it now, revisit the main site later)
Measured: **all six pages of www.hoelee.com** (home, `/zh-hans/`, `/about-mrhoelee/`, T&C, support, privacy) contain
**zero** references to `blog.hoelee.com`, while the blog links out to www.hoelee.com. The older, more established
@@ -152,7 +153,7 @@ nothing has ever been syndicated, so the 53 posts only exist for people who alre
- [ ] One LinkedIn share per flagship (same photo + name identity as the blog).
- **Done when:** each dev.to copy is live with the canonical set, and each has one LinkedIn post.
**Step E4 — Chinese-side routes and nav are broken.** ⬜ (extends Step C4)
**Step E4 — Chinese-side routes and nav are broken.** ✅ Done 2026-10-16 (batch B2), re-verified live 2026-10-17
Live ZH pages: the nav renders 「文章」→ `/zh/` (fallback; `/zh/posts/` **404s**) and 「关于」→ `/zh/` — i.e. the
Chinese nav's *About* link is wrong even though `/zh/about/` itself returns 200 and is in the sitemap. `grep
@@ -161,14 +162,14 @@ Chinese nav's *About* link is wrong even though `/zh/about/` itself returns 200
`/posts/zh/<slug>/` return 200 — there is no ZH post-archive route, so that path is answered by something else
entirely (CDN/nginx, not the static build). Not a regression from the publish; it is this same missing route.
- [ ] Build the `/zh/posts/` archive; point 「文章」 at it and 「关于」 at `/zh/about/`.
- **Done when:** `/zh/posts/` 200, both ZH nav labels point at the right routes, and `/zh/about/` is reachable by clicking from `/zh/`.
- [x] ✅ Built in batch B2 (`src/pages/zh/posts/index.astro`); nginx `301`s `/posts/zh/` → `/zh/posts/`; the ZH nav points 「文章」 → `/zh/posts/` and 「关于」 → `/zh/about/`.
- **Done when:** ✅ measured 2026-10-17 — `/zh/posts/` **200** (74 KB, the ZH archive with every row), `/zh/about/` 200, `/posts/zh/` 301, and both ZH nav links resolve when clicked.
**Step E5 — `og:locale` is wrong on all 53 Chinese pages.** ⬜
**Step E5 — `og:locale` is wrong on all 53 Chinese pages.** ✅ Done 2026-10-16 (batch B2), re-verified live 2026-10-17
ZH post pages emit `<meta property="og:locale" content="en">` with `og:locale:alternate = en_US`.
- **Done when:** ZH pages emit `zh_CN` (`en_US` as the alternate), EN pages unchanged.
- **Done when:** ✅ measured 2026-10-17 — a ZH post page emits `og:locale=zh_CN` with `og:locale:alternate=en_US`.
**Step E6 — Cloudflare is not caching the HTML.** ⬜
@@ -176,6 +177,10 @@ A post page returns `cf-cache-status: DYNAMIC` and `strict-transport-security: m
should be edge-cached — lower TTFB and less origin traffic through the tunnel.
- [ ] Add a cache rule for `blog.hoelee.com` (cache HTML, honour origin `last-modified`) and take a decision on HSTS.
⚠ **Owner action — the token available to the blog can only purge, it cannot write zone settings** (re-measured
2026-10-17: `cf-cache-status: DYNAMIC` on every page, `strict-transport-security: max-age=0; includeSubDomains; preload`).
Until the rule exists every page is rendered from the origin across the tunnel on every hit — the same work that shows up
as **1.3–5.7 s** public TTFB while the LAN origin answers in 3–20 ms.
- **Done when:** a repeat request shows `cf-cache-status: HIT` **and** a deploy still goes live within ~1 minute.
**Step E7 — Content-volume guardrail.** ⬜ ongoing
@@ -191,7 +196,11 @@ should be edge-cached — lower TTFB and less origin traffic through the tunnel.
- [ ] `scraping-bot-walled-marketplace-warm-browser-session`: OG card exists, **banner 404s** (no `banner:` in frontmatter, no PNG). Generate it or accept it as the one exception.
- [x] ✅ `authentik-css-greater-than-bug` (the `>` bug) — **published 2026-10-16 (`2295c0f`)**: `draft: true → false` in EN + ZH, `pubDate` kept at 2026-09-20 with **`updatedDate: 2026-10-16`** so `lastmod` stays honest, OG + banner generated (og `rows=1 overflow=0`; banner `delta=2 overflow=0`), and the CTA's displayed phone corrected (`011-797 2969` → `+60 12-797 2969`). Verified live: EN/ZH/og/banner all **200**, language switch both ways, `/posts/` order still monotonic. The draft bank is now empty.
- [ ] `og-default.png` has **no face**; the homepage and category pages share it. Regenerate after Step A1 (same source asset).
- [x] `og-default.png` rewritten 2026-10-17 (`scripts/og-gen/generate-default.mjs` + `scripts/og-gen/template-default.html`).
The card it replaces had shipped since 2026-09-06 still carrying the **retired brand name `hoelee.dev`** and no face, while the
headshot landed on the site in B4 — i.e. the most-linked share card was the least current asset on the site. New card:
headshot + `Lee Teong Hoe` / `Mr Hoelee` + the JSON-LD job title, measured in the DOM (`overflow=0`, 1200×630).
⏸ **Not committed yet — waiting for the owner's look.**
- **Done when:** no page references a 404 image, the draft bank is empty, and the default share card matches the A1 photo.
### Phase A — Identity (highest ROI, ~2–3 hrs total)
@@ -683,6 +692,32 @@ start, so publishing added no pages. The number in that message is wrong; the de
carry **no prose links** — every post they would naturally cite is dated later, and a backdated post may only link
backwards. Their "related posts" rows are the theme's automatic ones, which every backdated post already has.
**Step B2p — (unplanned) The commit-churn post, held for the read (written 2026-10-17 04:00).** 🔵
| Slug | Category | `pubDate` | Source |
|---|---|---|---|
| `why-500000-lines-of-commit-churn-only-left-230000-in-production` | `engineering` | 2026-10-17 | Hermes session 2026-10-17 |
- **What it argues:** thirty days of autonomous-agent work across five self-hosted Gitea repos = 865 commits, 518,233
additions, 130,892 deletions — and production kept 235,000 lines. Verification gates and pruning, not generation
speed, are what survive.
- **State (all of it measured 2026-10-17, not carried over from the writing session):** EN + ZH written;
og + banner generated and **measured** (`scripts/measure_og_banner.py` → OG `rows=1 overflow=0`, banner 7 rows
`delta=2 overflow=0`); local build clean at **250 `index.html` pages** with both `dist/` pages present; the generator
changes are **purely additive** (`git diff --numstat` 8/0 and 18/0, `node --check` OK on both).
**Not committed, not pushed** — four files (EN + ZH `.md`, og + banner PNG, plus the generator delta) sit in the working
tree on purpose: publication gate, the owner has not read it.
- ⚠ **This entry is the record of that hold.** Without it the next session reads the untracked files as someone else's
in-flight work (or as already-published) — the same failure the skill warns about.
- **Preview for the owner:** `cd dist && python -m http.server 8899 --bind 127.0.0.1`, then
`/posts/why-500000-lines-of-commit-churn-only-left-230000-in-production/` and its `/posts/zh/…` twin.
- **To publish on his go-ahead:** confirm the real date (`date` + the newest `<time>` on the live `/posts/`), then
`git add` the four explicit paths, commit, push `origin main` **and** `github main`, verify the four URLs 200 and the
`/posts/` order still monotonic. The `TERMINALS`/`BANNERS` entries ride along (whole-file staging) and stay inert until
that slug is generated.
- ⚠ **Kill the 8899 preview server once he has read it** — a wedged `python -m http.server` from the writing session was
still holding the port on 2026-10-17 and served an empty response.
**Step B2m — The next-session topic queue (user decision 2026-10-16).** ⬜
From the same sweep, **explicitly not to be written**: the Photoshop "closes every five minutes" post (#6 in the
@@ -795,6 +830,30 @@ not needed), cross-checked against the local repo at `D:/dev/hoelee-blog`.
---
## 4b. Live re-check — 2026-10-17 (after batches B1–B4)
Method: `curl` from the Windows host, the unRaid nginx access log **read over the Portainer API** (no SSH to that host),
and a DOM probe for the regenerated card. Spot values: `/zh/posts/` 200 · `/tags/docker/` 200 · `/llms.txt` 200
(48,386 B) · author photo live (`lee-teong-hoe-640|128`, WebP + JPEG) · no analytics beacon on any page ·
`cf-cache-status: DYNAMIC` + HSTS `max-age=0`.
| Check | Result |
|---|---|
| **Closed since the Sept audit** | A1 real photo ✅ (B4) · A3 featured strip ✅ (B3) · D1 search ✅ (B3 fixed the ES-module loader) · E4 ZH routes + nav ✅ (B2) · E5 `og:locale` ✅ (B2) · E8 draft bank ✅ (now empty) |
| **Still open** | E1 CF Web Analytics beacon + GSC sitemap (owner) · E6 HTML caching + HSTS (**CF zone write — owner**) · E8 `og-default.png` (rewritten, awaiting the owner's look) + the one banner-less post · A2 title/hero wording (cosmetic) · E2 (recorded, deferred by the owner 2026-10-17) · E3 syndication (needs dev.to + LinkedIn accounts) |
| **GitHub mirror** | was **24 commits behind** — nothing had been pushed since `d2b15d6`, so all of B1–B4 existed only on Gitea. Pushed 2026-10-17: `d2b15d6..f4b2ce9`, now **0/0** with both remotes |
| **`/posts/zh/` redirect scheme** | the 301 came back as `Location: http://blog.hoelee.com/zh/posts/` (nginx absolutised it with the scheme the tunnel reached it on), costing an extra Cloudflare "Always Use HTTPS" 308 hop. Fixed 2026-10-17 in the host nginx conf with `absolute_redirect off` (backup `default.conf.bak-20261017` beside it), validated in a throwaway container against the real file, `nginx -t` + reload in the live container — now `Location: /zh/posts/` on both the LAN origin and the public URL |
| **`/zh/posts/` 502 blip** | 3 short 502s inside one polling window, then **45/45 × 200** (15 each on `/`, `/posts/`, `/zh/posts/`). The nginx access log over 4 h holds **zero 5xx** (411 × 200 / 31 × 404 / 11 × 304 / 2 × 301) and the LAN origin answers in 3–20 ms ⇒ the 502s came from the tunnel/edge path, not from the page or the origin. Worth watching; not a defect in the archive |
| **TTFB** | origin 3–20 ms vs public 1.3–5.7 s — the measurable cost of having no edge HTML cache (E6) |
> Reminder the next session needs: the blog's nginx config is **not** in the repo — it is the host file
> `/mnt/user/docker/nginx-hoelee-blog/conf.d/default.conf` on unRaid, reachable through the **DSM Portainer** (endpoint 3,
> stack 247) because unRaid's root login refuses SSH keys. Read it from the running container
> (`docker exec nginx-hoelee-blog cat /etc/nginx/conf.d/default.conf`), write it with a helper container that bind-mounts
> `/mnt/user/docker/nginx-hoelee-blog` read-write, then `nginx -t` before `nginx -s reload`.
---
## 5. Conventions (non-negotiable)
- Push git.hoelee.com first, then GitHub