From 4b489a3ebb82811947195ddc040ef60f33b3d452 Mon Sep 17 00:00:00 2001 From: hanyixuanten Date: Sun, 25 Oct 2026 12:15:52 +0000 Subject: [PATCH] feat(api): auto-create and migrate messages table on first request ensureSchema runs idempotent CREATE TABLE / ALTER TABLE (skipping duplicate-column errors), cached once per isolate with retry on failure; schema.sql becomes reference-only documentation --- functions/api/messages.js | 44 ++++++++++++++++++++++++++++++++++++++- schema.sql | 9 +++----- 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/functions/api/messages.js b/functions/api/messages.js index cddd739..7b2fe00 100644 --- a/functions/api/messages.js +++ b/functions/api/messages.js @@ -2,7 +2,8 @@ * Cloudflare Pages Functions * GET /api/messages → 返回最新留言列表 * POST /api/messages → 新增一条留言 - * 依赖绑定:环境变量 DB(D1 数据库,建表语句见 /schema.sql) + * 依赖绑定:环境变量 DB(D1 数据库) + * 表结构由 ensureSchema 在首次访问时自动创建/迁移,无需手动执行 SQL */ const MAX_NAME = 12; @@ -40,6 +41,43 @@ function clean(str, max) { .slice(0, max); } +/* ---------- 表结构:自动建表/迁移(幂等,旧表缺列时自动补齐) ---------- */ +const SCHEMA_SQL = [ + `CREATE TABLE IF NOT EXISTS messages ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + text TEXT NOT NULL, + time TEXT NOT NULL, + ip TEXT, + ua TEXT, + country TEXT, + created_at INTEGER NOT NULL + )`, + 'ALTER TABLE messages ADD COLUMN ua TEXT', + 'ALTER TABLE messages ADD COLUMN country TEXT', +]; + +let schemaPromise = null; // 每个 isolate 只迁移一次;失败不缓存,下个请求重试 + +function ensureSchema(env) { + if (!schemaPromise) { + schemaPromise = (async () => { + for (const sql of SCHEMA_SQL) { + try { + await env.DB.prepare(sql).run(); + } catch (err) { + // 列已存在时 SQLite 报 duplicate column name,属预期 + if (!/duplicate column/i.test(String(err && err.message))) throw err; + } + } + })().catch((err) => { + schemaPromise = null; + throw err; + }); + } + return schemaPromise; +} + /* ---------- OPTIONS(预检) ---------- */ export async function onRequestOptions() { return new Response(null, { @@ -58,6 +96,8 @@ export async function onRequestGet({ env }) { if (!env.DB) return json({ ok: false, error: '数据库未绑定' }, 500); try { + await ensureSchema(env); + const { results } = await env.DB .prepare( `SELECT id, name, text, time @@ -109,6 +149,8 @@ export async function onRequestPost({ request, env }) { const now = Math.floor(Date.now() / 1000); try { + await ensureSchema(env); + if (ip) { const last = await env.DB .prepare( diff --git a/schema.sql b/schema.sql index 97642c0..bad1bc1 100644 --- a/schema.sql +++ b/schema.sql @@ -1,7 +1,8 @@ -- D1 数据库结构(留言墙 /api/messages 使用,绑定名 DB) -- --- 全新部署:直接执行下面的 CREATE TABLE。 --- 已有部署:老表缺少 ua / country 列时,执行文件末尾注释里的 ALTER TABLE。 +-- 无需手动执行:Functions 首次访问会自动建表并为旧表补齐缺失的列 +-- (见 functions/api/messages.js 的 ensureSchema)。本文件仅作结构参考, +-- 需要手工干预(如排查问题)时再执行。 CREATE TABLE IF NOT EXISTS messages ( id INTEGER PRIMARY KEY AUTOINCREMENT, @@ -13,7 +14,3 @@ CREATE TABLE IF NOT EXISTS messages ( country TEXT, -- 国家/地区代码,来自 CF-IPCountry(审计) created_at INTEGER NOT NULL -- Unix 秒,排序与限流用 ); - --- 已有部署的增量迁移: --- ALTER TABLE messages ADD COLUMN ua TEXT; --- ALTER TABLE messages ADD COLUMN country TEXT;