docs(project-state): E3 queue 10 -> 69 with draft/HTTP guards, E6 edge caching measured working (Cache Rule HIT + purge proven in the run log)
Deploy / build (push) Successful in 59s
Deploy / build (push) Successful in 59s
This commit is contained in:
1 parent
112cf7de62
commit
e6dee0dd57
1 file changed
+29
-15
+29
-15
@@ -154,8 +154,13 @@ the URL — the daily dev.to drip below is the first distribution channel outsid
|
||||
- [x] ✅ **dev.to cross-posting is automated (2026-10-17).** `scripts/devto-crosspost.py` in the Hermes scripts dir
|
||||
(`C:\Users\hoelee\AppData\Local\hermes\scripts\`) publishes **one queued post per day** with `canonical_url`
|
||||
pointing at the blog original, from a Hermes **no_agent cron** (`devto daily crosspost`, id `ce8fc8bd23e6`,
|
||||
`25 12 * * *`, deliver Telegram). Queue = 10 ranked titles in `devto-queue.json`; `devto-state.json` makes it
|
||||
idempotent per slug; the API key lives in `~/.secrets/devto-api-key.txt`, never in the repo.
|
||||
`25 12 * * *`, deliver Telegram). Queue = **69 ranked titles / every live post except three** — `hello-world` (blog intro page),
|
||||
`how-i-made-my-own-songs-with-suno-ai` (8 `<audio>` players + a lyric `<script>` that dev.to's sanitiser strips)
|
||||
and the held-for-review post — at **one per day ≈ 2.3 months**. `devto-state.json` makes it idempotent per slug;
|
||||
the API key lives in `~/.secrets/devto-api-key.txt`, never in the repo. **Two guards added the same day:** an
|
||||
entry is refused while its frontmatter still says `draft: true` or its blog URL is not HTTP 200 (a transient
|
||||
5xx after 3 retries aborts the whole run instead of skipping, so queue order is never reshuffled by a tunnel
|
||||
blip), and `--validate` re-checks all 69 entries read-only (last run: **69 entries, 1 published, 0 blocked**).
|
||||
First article live and verified: **https://dev.to/hoelee/how-i-built-the-digikedai-telegram-ai-bot-39of** —
|
||||
`canonical_url` matches the blog post in the API read-back **and** in the live page's `<link rel="canonical">`,
|
||||
tags `ai, python, showdev, automation`, cover image re-hosted by dev.to (so the banner landed).
|
||||
@@ -182,17 +187,26 @@ ZH post pages emit `<meta property="og:locale" content="en">` with `og:locale:al
|
||||
|
||||
- **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.** ⬜
|
||||
**Step E6 — Cloudflare edge HTML caching.** 🔵 Cache Rule live 2026-10-17 and **measured working**; the HSTS half is still open
|
||||
|
||||
A post page returns `cf-cache-status: DYNAMIC` and `strict-transport-security: max-age=0`. A static site behind CF
|
||||
should be edge-cached — lower TTFB and less origin traffic through the tunnel.
|
||||
A post page used to return `cf-cache-status: DYNAMIC`. A static site behind CF 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.
|
||||
- [x] ✅ **Cache Rule in place (owner action, 2026-10-17: Edge TTL → *Override origin* → 2 hours; the dashboard offers no
|
||||
30-minute preset, which is why 2 h was picked — freshness comes from the deploy purge, not from the TTL).**
|
||||
Measured the same day: 10 sequential requests to `/posts/` → **9 × `HIT` / 1 × `MISS`**, ages 7–49 s; a post page
|
||||
`MISS → HIT (age 1 s) → HIT (age 661 s)`, TTFB 0.26–0.31 s on HIT vs 0.55 s on the cold MISS.
|
||||
⚠ **Two single-shot `MISS`es are NOT evidence the rule is broken.** Cloudflare's edge cache is per data centre
|
||||
(`CF-RAY` showed NRT then SIN inside the same minute), so `MISS` twice in a row only means two caches were read from.
|
||||
Judge caching with **≥ 8 sequential requests** (and read `age`), never with two.
|
||||
- [x] ✅ **The deploy-side purge is proven live, not assumed.** The Gitea Actions run for `112cf7d` (task 252 / run 11313,
|
||||
job 22965) logs `Purging zone *** … {"success":true,"errors":[],"messages":[],"result":{"id":"…"}}` — `CF_TOKEN` and
|
||||
`CF_ZONE_ID` are set and the API call succeeded, so a publish is not stuck behind the 2 h Edge TTL.
|
||||
- [ ] **HSTS is still `max-age=0; includeSubDomains; preload`** (re-measured 2026-10-17) — the decision (12 months,
|
||||
`includeSubDomains`, **preload OFF**) is still an owner action. `max-age=0` makes the `preload` directive inert,
|
||||
so nothing is broken today; the zone setting is simply not doing anything yet.
|
||||
- **Done when:** a repeat request shows `cf-cache-status: HIT` ✅ **and** a deploy still goes live within ~1 minute
|
||||
(purge proven ✅ — end-to-end re-tested on the next real publish) **and** HSTS carries the decided value ⬜.
|
||||
|
||||
**Step E7 — Content-volume guardrail.** ⬜ ongoing
|
||||
|
||||
@@ -854,7 +868,7 @@ not needed), cross-checked against the local repo at `D:/dev/hoelee-blog`.
|
||||
| ZH nav | 「文章」→ `/zh/` (no `/zh/posts/` route: **404**) and 「关于」→ `/zh/` — while `/zh/about/` is 200 but **orphaned** (zero inbound links) → **E4 / C4** |
|
||||
| `og:locale` | ZH pages emit `en` + alternate `en_US` → **E5** |
|
||||
| Homepage | No featured/start-here strip (**A3**). `<title>` says "engineering, DevOps & self-hosting" while the hero says "full-stack developer and DevOps engineer" (**A2**, cosmetic — lowest priority) |
|
||||
| Caching | `cf-cache-status: DYNAMIC` on a post page; `strict-transport-security: max-age=0` → **E6** |
|
||||
| Caching | `cf-cache-status: DYNAMIC` on a post page (✅ **now HIT** — owner's Cache Rule, measured 2026-10-17, §4b); `strict-transport-security: max-age=0` → **E6** |
|
||||
| Tags | Tags render as plain labels (not links), so `/tags/<tag>/` 404s cause **no broken links** — C2 is a missed-discovery item, not a bug |
|
||||
| Drafts | Exactly one: `authentik-css-greater-than-bug` (EN + ZH) → **E8** |
|
||||
| Cadence | 30 posts dated 2026-09; worst single-day stack = **5** (2026-09-18) — the backdating work held, no single-day dump at the top of the feed |
|
||||
@@ -867,16 +881,16 @@ not needed), cross-checked against the local repo at `D:/dev/hoelee-blog`.
|
||||
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`.
|
||||
`cf-cache-status: DYNAMIC` + HSTS `max-age=0` when the spot checks ran; the owner's Cache Rule landed later the same day, so see the table below for the re-measure.
|
||||
|
||||
| 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` (**rejected once; being designed outside the repo — brief on the Desktop**) + the one banner-less post · E9 the design system names two fonts the site never loads · A2 title/hero wording (cosmetic) · E2 (recorded, deferred by the owner 2026-10-17) · E3 syndication (dev.to ✅ automated 2026-10-17 · LinkedIn still needs the account) |
|
||||
| **Still open** | E1 CF Web Analytics beacon + GSC sitemap (owner) · E6 HTML edge caching ✅ (owner's Cache Rule — measured HIT 2026-10-17) + HSTS still `max-age=0` (owner) · E8 `og-default.png` (**rejected once; being designed outside the repo — brief on the Desktop**) + the one banner-less post · E9 the design system names two fonts the site never loads · A2 title/hero wording (cosmetic) · E2 (recorded, deferred by the owner 2026-10-17) · E3 syndication (dev.to ✅ automated 2026-10-17 · LinkedIn still needs the account) |
|
||||
| **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) |
|
||||
| **TTFB** | origin 3–20 ms vs public **1.3–5.7 s before** the edge cache (E6). After the owner's Cache Rule landed the same day: **0.10–0.58 s** on `cf-cache-status: HIT` (cold `MISS` 0.55–0.74 s) |
|
||||
|
||||
> 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,
|
||||
|
||||
Reference in new issue
Block a user