post: two reader/gateway posts — the upstream rewrite, and the front-door pattern (EN+ZH) + og/banner images
Deploy / build (push) Successful in 22s

1) the-upstream-was-deleted-then-came-back-rewritten (devops, pubDate 2026-10-06)
   docker pull hectorqin/reader -> 404; the repo was re-initialised 2026-09-16 and now
   carries 233 commits, a TypeScript/Node rewrite, port 5888, SQLite /data, config moved
   into the admin UI, and an image on cnb.cool with no tags or releases. Covers telling
   dead from rewriting, the migration boundary, and keeping the front door outside the
   app image. Facts re-verified live 2026-10-06.

2) putting-a-front-door-on-an-app-you-cant-modify (devops, pubDate 2025-06-24 backdated
   into the empty 2025-06 window, updatedDate 2026-10-06 so lastmod stays honest)
   The reader-gateway pattern: a second nginx container owning the public port, the
   request-time resolver, why a proxy body rewrite cannot touch a client-rendered SPA,
   the gate-bounce injection with its pass token, the app's own CSS hook for branding,
   and the silent-failure check to run after every app upgrade.

Both: EN + ZH twins, TERMINALS/BANNERS entries appended via append_generator_entries.py
(additive, deletions 0, node --check OK), og/banner generated and measured PASS
(og rows=1 overflow=0 missingHash=0; banner 8 rows, delta 2, overflow 0).
This commit is contained in:
2026-10-06 17:19:46 +08:00
parent 67b844e3a0
commit 378d55c3c8
10 changed files with 732 additions and 0 deletions
+37
View File
@@ -1112,6 +1112,43 @@ BANNERS['running-production-infrastructure-solo'] = {
],
};
BANNERS['the-upstream-was-deleted-then-came-back-rewritten'] = {
titlebar: "root@dsm — reader · the upstream came back",
lines: [
{ t: 'prompt', text: "$" }, { t: 'cmd', text: "docker pull hectorqin/reader" },
{ t: 'err', text: "→ 404 the official image is gone from Docker Hub" }, { t: 'prompt', text: "$" },
{ t: 'cmd', text: "git log --oneline | tail -1" }, { t: 'err', text: "233 commits, oldest: 初始化仓库 (2026-09-16)" },
{ t: 'hl', text: "Kotlin/Spring + Vert.x → TypeScript/Node · :8080 → :5888" }, { t: 'dim', text: "registry moved to cnb.cool — no tags, no releases" },
{ t: 'prompt', text: "$" }, { t: 'cmd', text: "keep the front door outside the app image" },
{ t: 'ok', text: "→ readers unaffected · migration plan written ✓" },
],
flow: [
{ n: '1', label: "orphaned image" },
{ n: '2', label: "404 on Docker Hub", err: true },
{ n: '3', label: "233 commits, new language" },
{ n: '4', label: "a rewrite = a new app" },
{ n: '5', label: "tested migration plan ✓" },
],
};
BANNERS['putting-a-front-door-on-an-app-you-cant-modify'] = {
titlebar: "root@dsm — reader-gateway · nginx:alpine",
lines: [
{ t: 'prompt', text: "$" }, { t: 'cmd', text: "curl -s book.example.com/index.html | wc -c" },
{ t: 'err', text: "5879 — a shell: one empty div, everything else drawn by JS" }, { t: 'prompt', text: "$" },
{ t: 'cmd', text: "body-rewrite? · patch the bundle? · fork it?" }, { t: 'err', text: "nothing in the bytes to rewrite" },
{ t: 'hl', text: "a second container owns the public port" }, { t: 'cmd', text: "location = / → my page · location / → the app · resolver at request time" },
{ t: 'dim', text: "app port → 127.0.0.1:7778 · branding via the app's own CSS hook" }, { t: 'ok', text: "→ front page · og card · 中/EN · vendor links gone ✓" },
],
flow: [
{ n: '1', label: "blank login box" },
{ n: '2', label: "client-rendered SPA", err: true },
{ n: '3', label: "gateway container" },
{ n: '4', label: "page + 3 injections" },
{ n: '5', label: "front door ✓" },
],
};
// ---------- read frontmatter ----------
const postPath = join(ROOT, 'src', 'content', 'posts', `${slug}.md`);
let category = 'devops';
+12
View File
@@ -337,6 +337,18 @@ TERMINALS['running-production-infrastructure-solo'] = `
<div class="line"><span class="prompt"> </span><span class="err">grafana: one NAS volume counted three times</span></div>
<div class="line"><span class="prompt">$</span><span class="cmd">verify_infra.py — re-derive from the live daemons</span><span class="fix">→ no remembered numbers ✓</span></div>`;
TERMINALS['the-upstream-was-deleted-then-came-back-rewritten'] = `
<div class="line"><span class="prompt">$</span><span class="cmd">docker pull hectorqin/reader</span><span class="err">→ 404 Not Found</span></div>
<div class="line"><span class="prompt">&nbsp;</span><span class="err">my 3.x build survives in a stranger's Docker Hub namespace</span></div>
<div class="line"><span class="prompt">$</span><span class="cmd">git log --oneline | wc -l</span><span class="fix">→ 233 commits since 2026-09-16</span></div>
<div class="line"><span class="prompt">$</span><span class="cmd">port · volume · env · db engine · config store</span><span class="err">→ all five changed: it's a new app</span></div>`;
TERMINALS['putting-a-front-door-on-an-app-you-cant-modify'] = `
<div class="line"><span class="prompt">$</span><span class="cmd">curl -s book.example.com/index.html | wc -c</span><span class="err">→ 5879 · body is <div id="app"></div></span></div>
<div class="line"><span class="prompt">&nbsp;</span><span class="err">client-rendered → a proxy body rewrite has nothing to rewrite</span></div>
<div class="line"><span class="prompt">$</span><span class="cmd">docker compose up -d (gateway takes the public port)</span><span class="fix">→ front page 200 ✓</span></div>
<div class="line"><span class="prompt">$</span><span class="cmd">curl -s /index.html | grep -o lang=</span><span class="fix">→ zh-CN ✓</span></div>`;
// ---------- read frontmatter ----------
const postPath = join(ROOT, 'src', 'content', 'posts', `${slug}.md`);
if (!existsSync(postPath)) {