2 Commits
Author SHA1 Message Date
wait4xandGLM-5.3 810cf44d3d fix(deploy): receiver 改调构建仓自己的 build.sh——发布链执行源归一
- 架构债(20:31 事故根因层):receiver 此前执行工作树
  infra/deploy/build.sh,车道在途半成品状态可断生产链——改为
  build-repo/infra/deploy/build.sh(origin/main 硬跟版本),
  构建仓缺失时首克引导(匿名 clone)
- 双副本纪律入档头注:仓内源与 ~/docker_apps/blog/ 运行副本改一
  须同步二并 restart(systemd 跑的是运行副本,漂移即事故)
- 深意:构建脚本演化自此走 git 评审流,main=可部署铁律闭环

验证: 签名 POST 端到端实测(HMAC 构造无 git 污染):202 queued →
      90s 后 stamp=1fdd0cc;healthz 200;单元 active

Spec: none (发布链架构修正:单执行源切换+端到端实测)

Co-authored-by: GLM-5.3
2026-10-25 21:12:48 +08:00
wait4xandGLM-5.3 0c569b0b31 feat(i18n): Keyboard 帮助表全量双语——构建期 localeOf 自推断
- 10 词条+标题+hint 全走 t(key, locale)(ui.ts 新增 kbd_* 11 键,
  零文案字面量复制);en/terminal 车道的运行时 replaceChildren
  补丁退役(构建期已渲染正确文案)
- hint 行精简:删过程性标注"Phase 4 上线 ~"(~ 并非实际绑定,
  有误导),改 /terminal 彩蛋引导语
- 已知边界另车道:g-chords 路由表仍 zh 路径(en 页 g h 跳 / 而非
  /en/)——routes 表 locale 化待办

车道:op-artalk(自验 7/7:zh/en 深层路由自感知/inField 旁路
(?正常入框不弹层)/blur 回归);收编验收 dialog 作用域复验全过
(首验 2 FAIL 均系验收方断言姿势:大小写+全 body 作用域,非车道
缺陷——验收先探实值再断言的教训第三次入档)

验证: pnpm astro build 34 页 exit 0;E2E 31/31 PASS;en 深层路由
      /en/terminal/ 帮助表纯英文实测

Spec: none (组件双语化,车道级交付)

Co-authored-by: GLM-5.3
2026-10-25 21:12:48 +08:00
3 changed files with 45 additions and 18 deletions

No files matched your search

+17 -3
View File
@@ -8,6 +8,8 @@
ESA 只代理 80/443 vhost,该端口不对公网;HMAC 是硬门槛。 ESA 只代理 80/443 vhost,该端口不对公网;HMAC 是硬门槛。
触发链:gitea push(main) → 本接收器验签 → build.sh → www-staging 更新。 触发链:gitea push(main) → 本接收器验签 → build.sh → www-staging 更新。
部署形态:systemd 运行副本 ~/docker_apps/blog/webhook_receiver.py——改本文件
必须同步两份(仓内源 + 运行副本)并 restart 单元,漂移即事故。
常驻方式:systemd --user(blog-webhook.service);reboot 自启需一次性 常驻方式:systemd --user(blog-webhook.service);reboot 自启需一次性
`sudo loginctl enable-linger wait4x`(已列入上线检查单)。 `sudo loginctl enable-linger wait4x`(已列入上线检查单)。
""" """
@@ -21,9 +23,21 @@ from pathlib import Path
PORT = 13021 PORT = 13021
SECRET_FILE = Path.home() / "docker_apps/gitea/webhook-secret.txt" SECRET_FILE = Path.home() / "docker_apps/gitea/webhook-secret.txt"
BUILD = Path.home() / "Code/Github/my-blog/infra/deploy/build.sh" BUILD_REPO = Path.home() / "docker_apps/blog/build-repo"
BUILD_LOCK = threading.Lock() BUILD_LOCK = threading.Lock()
# 构建脚本 = 构建仓自己的 origin/main 版本(架构修正 2026-10-25:此前指向
# 工作树副本,车道在途半成品状态会断生产链——20:31 事故实锤)。构建仓缺失时
# 首克引导(blog 仓公开,匿名 clone)。
def build_script() -> Path:
s = BUILD_REPO / "infra/deploy/build.sh"
if not s.exists():
subprocess.run(
["git", "clone", "https://git.wait4x.online/wait4x/blog.git", str(BUILD_REPO)],
check=True, capture_output=True,
)
return s
def read_secret() -> bytes: def read_secret() -> bytes:
return SECRET_FILE.read_text().strip().encode() return SECRET_FILE.read_text().strip().encode()
@@ -72,9 +86,9 @@ class Handler(BaseHTTPRequestHandler):
def _run_build(self) -> None: def _run_build(self) -> None:
with BUILD_LOCK: with BUILD_LOCK:
subprocess.run( subprocess.run(
["bash", str(BUILD)], ["bash", str(build_script())],
capture_output=True, timeout=1200, capture_output=True, timeout=1200,
cwd=str(BUILD.parent), cwd=str(BUILD_REPO),
) )
def log_message(self, fmt: str, *args: object) -> None: def log_message(self, fmt: str, *args: object) -> None:
+15 -15
View File
@@ -1,6 +1,10 @@
--- ---
// 键盘层(极客层 P0):? 帮助 / t 主题 / g-chords 导航 // 键盘层(极客层 P0):? 帮助 / t 主题 / g-chords 导航
// 惯例来源:GitHub 键位(g+N、? 帮助);每项能力都有可视入口(触屏等价物原则) // 惯例来源:GitHub 键位(g+N、? 帮助);每项能力都有可视入口(触屏等价物原则)
// 文案全部 ui.ts 词条:locale 构建期按页面 URL 自推断(BaseLayout 不透传,同 Footer 模式)
import { t } from '../../i18n/ui';
import { localeOf } from '../../lib/locale';
const locale = localeOf(Astro.url.pathname);
const routes: Record<string, string> = { const routes: Record<string, string> = {
h: '/', h: '/',
a: '/archives/', a: '/archives/',
@@ -11,21 +15,21 @@ const routes: Record<string, string> = {
--- ---
<dialog id="kbd-help" class="kbd-help" data-routes={JSON.stringify(routes)}> <dialog id="kbd-help" class="kbd-help" data-routes={JSON.stringify(routes)}>
<h2>键盘快捷键</h2> <h2>{t('kbd_title', locale)}</h2>
<table> <table>
<tbody> <tbody>
<tr><td><kbd>Ctrl</kbd> <kbd>K</kbd></td><td>命令面板</td></tr> <tr><td><kbd>Ctrl</kbd> <kbd>K</kbd></td><td>{t('kbd_palette', locale)}</td></tr>
<tr><td><kbd>t</kbd></td><td>切换明暗</td></tr> <tr><td><kbd>t</kbd></td><td>{t('kbd_theme_toggle', locale)}</td></tr>
<tr><td><kbd>?</kbd></td><td>本帮助</td></tr> <tr><td><kbd>?</kbd></td><td>{t('kbd_this_help', locale)}</td></tr>
<tr><td><kbd>g</kbd> <kbd>h</kbd></td><td>首页</td></tr> <tr><td><kbd>g</kbd> <kbd>h</kbd></td><td>{t('kbd_gh_home', locale)}</td></tr>
<tr><td><kbd>g</kbd> <kbd>a</kbd></td><td>文章</td></tr> <tr><td><kbd>g</kbd> <kbd>a</kbd></td><td>{t('kbd_ga_posts', locale)}</td></tr>
<tr><td><kbd>g</kbd> <kbd>g</kbd></td><td>图库</td></tr> <tr><td><kbd>g</kbd> <kbd>g</kbd></td><td>{t('kbd_gg_gallery', locale)}</td></tr>
<tr><td><kbd>g</kbd> <kbd>m</kbd></td><td>瞬间</td></tr> <tr><td><kbd>g</kbd> <kbd>m</kbd></td><td>{t('kbd_gm_moments', locale)}</td></tr>
<tr><td><kbd>/terminal</kbd></td><td class="k-term-desc">终端彩蛋</td></tr> <tr><td><kbd>/terminal</kbd></td><td>{t('kbd_terminal', locale)}</td></tr>
<tr><td><kbd>Esc</kbd></td><td>关闭浮层</td></tr> <tr><td><kbd>Esc</kbd></td><td>{t('kbd_esc_close', locale)}</td></tr>
</tbody> </tbody>
</table> </table>
<p class="hint">提示:试试终端彩蛋(Phase 4 上线 <code>~</code>)</p> <p class="hint">{t('kbd_hint', locale)}</p>
</dialog> </dialog>
<script> <script>
@@ -33,10 +37,6 @@ const routes: Record<string, string> = {
// 路由表由 data-routes 注入(module 脚本里 currentScript 恒为 null,不能走内联取参) // 路由表由 data-routes 注入(module 脚本里 currentScript 恒为 null,不能走内联取参)
const helpEl = document.getElementById('kbd-help') as HTMLElement & { dataset: { routes?: string } }; const helpEl = document.getElementById('kbd-help') as HTMLElement & { dataset: { routes?: string } };
const routes: Record<string, string> = JSON.parse(helpEl?.dataset.routes ?? '{}'); const routes: Record<string, string> = JSON.parse(helpEl?.dataset.routes ?? '{}');
// en 轨仅切换终端行词条(帮助表其余文案的双语化另行车道)
if (location.pathname === '/en' || location.pathname.startsWith('/en/')) {
document.querySelector('.k-term-desc')?.replaceChildren(document.createTextNode('terminal'));
}
let chord = ''; let chord = '';
let chordTimer: number | undefined; let chordTimer: number | undefined;
+13
View File
@@ -78,6 +78,19 @@ const dict = {
footer_stack: { zh: '装备', en: 'stack' }, footer_stack: { zh: '装备', en: 'stack' },
footer_colophon: { zh: '制作', en: 'colophon' }, footer_colophon: { zh: '制作', en: 'colophon' },
// ===== C 车道:Keyboard 帮助表全量词条(组件 Astro.url 构建期自推断 locale) =====
kbd_title: { zh: '键盘快捷键', en: 'Keyboard shortcuts' },
kbd_palette: { zh: '命令面板', en: 'Command palette' },
kbd_theme_toggle: { zh: '切换明暗', en: 'Toggle light / dark' },
kbd_this_help: { zh: '本帮助', en: 'This help' },
kbd_gh_home: { zh: '首页', en: 'Home' },
kbd_ga_posts: { zh: '文章', en: 'Posts' },
kbd_gg_gallery: { zh: '图库', en: 'Gallery' },
kbd_gm_moments: { zh: '瞬间', en: 'Moments' },
kbd_terminal: { zh: '终端彩蛋', en: 'Terminal easter egg' },
kbd_esc_close: { zh: '关闭浮层', en: 'Close dialogs' },
kbd_hint: { zh: '提示:/terminal 终端彩蛋等你探索', en: 'Tip: the /terminal easter egg awaits' },
// ===== 命令面板(Phase 4 T1 车道):动作/导航/分组与键位提示 ===== // ===== 命令面板(Phase 4 T1 车道):动作/导航/分组与键位提示 =====
nav_home: { zh: '首页', en: 'Home' }, nav_home: { zh: '首页', en: 'Home' },
nav_moments: { zh: '瞬间', en: 'Moments' }, nav_moments: { zh: '瞬间', en: 'Moments' },