From 7df50e0dc37aef9b9c56dd4cd3eb0bd3c7a7de14 Mon Sep 17 00:00:00 2001 From: kang Date: Sun, 19 Apr 2026 21:17:01 +0800 Subject: [PATCH] phase 5 prep: backfill-users.ts + deployment runbook in .memory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - scripts/backfill-users.ts: 扫 PG users 表,对每人幂等调 orchestrator POST /users - orchestrator/package.json: 加 postgres 依赖 - .memory/project.md: Phase 3/4 完成状态,Phase 5 上线 checklist Co-Authored-By: Claude Opus 4.7 (1M context) --- .memory/project.md | 35 +++++++++++++++++------ .memory/worklog.json | 7 +++++ orchestrator/package.json | 1 + scripts/backfill-users.ts | 59 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 93 insertions(+), 9 deletions(-) create mode 100644 scripts/backfill-users.ts diff --git a/.memory/project.md b/.memory/project.md index 160b69a..07aa808 100644 --- a/.memory/project.md +++ b/.memory/project.md @@ -81,18 +81,35 @@ Debian 13 默认 Python 3.13(不是计划的 3.12)。不影响功能,更新。 - **不预装** pandas/numpy/torch/playwright/ffmpeg/libreoffice - LLM 按需 `uv pip install xxx` — 后续加本地 PyPI 缓存镜像让它快到秒级(尚未做,列在 TODO) -## 进度(2026-04-18) +## 进度(2026-04-19 更新) - ✅ Phase 0 Pre-flight(VPS 摸底:31GB RAM / 387GB 磁盘 / Incus 6.0.0 / hermes-box 已跑) - ✅ Phase 1 宿主初始化(btrfs pool + project + profile + **UFW iptables 修复并持久化**) -- 🔄 **Phase 2 base 镜像构建**(apt 完成,Node 20 安装中/卡) - - 已装:python3.13, build-essential, git, curl, ripgrep, fd, bat, 中文字体, tzdata, locale - - 已装:nodejs 20.20.2(via NodeSource) - - **待装**:corepack + uv + bun + sandbox 用户 + /workspace + publish - - **已踩的坑**:用 `nohup bash -s < + * bun run scripts/backfill-users.ts + */ +import postgres from 'postgres'; + +const DATABASE_URL = process.env.DATABASE_URL; +const BACKEND_URL = process.env.SANDBOX_BACKEND_URL ?? 'http://127.0.0.1:8700'; +const SECRET = process.env.SANDBOX_BACKEND_SECRET; + +if (!DATABASE_URL) throw new Error('DATABASE_URL required'); +if (!SECRET) throw new Error('SANDBOX_BACKEND_SECRET required'); + +const sql = postgres(DATABASE_URL, { max: 1 }); + +const users: { id: string; email: string | null }[] = await sql` + SELECT id, email FROM users ORDER BY created_at ASC +`; + +console.log(`[backfill] ${users.length} users to check`); + +let ok = 0, + skip = 0, + fail = 0; + +for (const u of users) { + try { + const res = await fetch(`${BACKEND_URL}/api/v1/users`, { + body: JSON.stringify({ userId: u.id }), + headers: { 'Content-Type': 'application/json', 'X-Sandbox-Secret': SECRET }, + method: 'POST', + }); + const text = await res.text(); + if (res.ok) { + if (text.includes('"success":true')) { + ok++; + console.log(`[backfill] ✓ ${u.id} (${u.email ?? '-'})`); + } else { + skip++; + console.log(`[backfill] ~ ${u.id}: ${text.slice(0, 100)}`); + } + } else { + fail++; + console.error(`[backfill] ✗ ${u.id}: HTTP ${res.status} ${text.slice(0, 100)}`); + } + } catch (e) { + fail++; + console.error(`[backfill] ✗ ${u.id}:`, (e as Error).message); + } +} + +console.log(`[backfill] done: ok=${ok} skip=${skip} fail=${fail}`); +await sql.end();