@whispering233/ai-editor-server 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/client-dist/assets/index-Bi1eymph.js +14 -0
  2. package/client-dist/assets/index-j-zzWAuh.css +1 -0
  3. package/client-dist/index.html +22 -0
  4. package/dist/consistency.d.ts +33 -0
  5. package/dist/consistency.d.ts.map +1 -0
  6. package/dist/consistency.js +53 -0
  7. package/dist/consistency.js.map +1 -0
  8. package/dist/debug.d.ts +29 -0
  9. package/dist/debug.d.ts.map +1 -0
  10. package/dist/debug.js +105 -0
  11. package/dist/debug.js.map +1 -0
  12. package/dist/index.d.ts +61 -0
  13. package/dist/index.d.ts.map +1 -0
  14. package/dist/index.js +284 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/middleware/error.d.ts +40 -0
  17. package/dist/middleware/error.d.ts.map +1 -0
  18. package/dist/middleware/error.js +64 -0
  19. package/dist/middleware/error.js.map +1 -0
  20. package/dist/middleware/project.d.ts +67 -0
  21. package/dist/middleware/project.d.ts.map +1 -0
  22. package/dist/middleware/project.js +146 -0
  23. package/dist/middleware/project.js.map +1 -0
  24. package/dist/routes/chat.d.ts +98 -0
  25. package/dist/routes/chat.d.ts.map +1 -0
  26. package/dist/routes/chat.js +478 -0
  27. package/dist/routes/chat.js.map +1 -0
  28. package/dist/routes/delta.d.ts +4 -0
  29. package/dist/routes/delta.d.ts.map +1 -0
  30. package/dist/routes/delta.js +144 -0
  31. package/dist/routes/delta.js.map +1 -0
  32. package/dist/routes/entity.d.ts +7 -0
  33. package/dist/routes/entity.d.ts.map +1 -0
  34. package/dist/routes/entity.js +126 -0
  35. package/dist/routes/entity.js.map +1 -0
  36. package/dist/routes/outline.d.ts +11 -0
  37. package/dist/routes/outline.d.ts.map +1 -0
  38. package/dist/routes/outline.js +250 -0
  39. package/dist/routes/outline.js.map +1 -0
  40. package/dist/routes/project.d.ts +27 -0
  41. package/dist/routes/project.d.ts.map +1 -0
  42. package/dist/routes/project.js +552 -0
  43. package/dist/routes/project.js.map +1 -0
  44. package/dist/routes/proposal.d.ts +4 -0
  45. package/dist/routes/proposal.d.ts.map +1 -0
  46. package/dist/routes/proposal.js +145 -0
  47. package/dist/routes/proposal.js.map +1 -0
  48. package/dist/routes/relation.d.ts +6 -0
  49. package/dist/routes/relation.d.ts.map +1 -0
  50. package/dist/routes/relation.js +87 -0
  51. package/dist/routes/relation.js.map +1 -0
  52. package/dist/routes/settings.d.ts +37 -0
  53. package/dist/routes/settings.d.ts.map +1 -0
  54. package/dist/routes/settings.js +91 -0
  55. package/dist/routes/settings.js.map +1 -0
  56. package/dist/routes/trash.d.ts +4 -0
  57. package/dist/routes/trash.d.ts.map +1 -0
  58. package/dist/routes/trash.js +135 -0
  59. package/dist/routes/trash.js.map +1 -0
  60. package/package.json +48 -0
@@ -0,0 +1,552 @@
1
+ // 项目路由(S1.2):POST /create、POST /open、POST /close、GET/PUT /config、GET /export(E1)、POST /import(E2)
2
+ //
3
+ // 契约来源:doc/api/endpoints.md 第 18-122 行(项目管理全部端点)、doc/design/decisions.md
4
+ // 决策 8(单进程 currentProject)、决策 13 修订(open 时 user_version 判定删库重建)、决策 17(路径校验防越权)。
5
+ // 校验失败统一 400 INVALID_PROJECT_PATH(shared ErrorCode,endpoints.md 第 66 行)。
6
+ import { isAbsolute, join, resolve } from "node:path";
7
+ import { tmpdir } from "node:os";
8
+ import { copyFileSync, existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, realpathSync, rmSync, statSync, writeFileSync, } from "node:fs";
9
+ import { Hono } from "hono";
10
+ import { Unzip, UnzipInflate, zipSync } from "fflate";
11
+ import { mapProjectFileToConfig } from "@whispering233/ai-editor-shared";
12
+ import { PROJECT_EXPORT_FILE_NAMES } from "@whispering233/ai-editor-shared/schemas";
13
+ import { openDatabase, closeDatabase, getUserVersion, SCHEMA_VERSION } from "@whispering233/ai-editor-db";
14
+ import { ensureSchemaCompatible, DATA_DB_FILE_NAME, hasMigrationPath } from "@whispering233/ai-editor-db";
15
+ import { SchemaVersionError } from "@whispering233/ai-editor-db";
16
+ import { OUTLINE_FILE_NAME } from "@whispering233/ai-editor-db";
17
+ import { PROJECT_FILE_NAME } from "@whispering233/ai-editor-db";
18
+ import { checkpointWal } from "@whispering233/ai-editor-db";
19
+ import { findOutlineNode, readOutlineFile, readProjectFile, writeProjectFile } from "@whispering233/ai-editor-db";
20
+ import { nowIso } from "@whispering233/ai-editor-db";
21
+ import { projectConfigUpdateReqSchema, projectCreateReqSchema, projectListResSchema, projectOpenReqSchema, } from "@whispering233/ai-editor-shared/schemas";
22
+ import { HttpError, ok } from "../middleware/error.js";
23
+ import { closeProject, getCurrentProject, initProject, requireCurrentProject, setCurrentProject, } from "../middleware/project.js";
24
+ import { logSoftDeleteReconcile, reconcileSoftDelete } from "../consistency.js";
25
+ // ============ 创作根(书架模式 S1.5) ============
26
+ //
27
+ // 语义:启动目录 = 创作根(书架),每本书 = 创作根/books/<书名>/(含 project.json 等三文件)。
28
+ // 创作根是 server 启动参数(index.ts startServer 的 projectRoot),与 currentProject(运行态)
29
+ // 不同维度——list 端点**不依赖 currentProject**(书架模式待命时无当前项目也要能列书),
30
+ // 因此以模块级状态持有,startServer 挂载路由前经 setProjectRoot 注入。
31
+ /** 创作根绝对路径(startServer 注入;null = 未初始化,list 端点防御性 500) */
32
+ let projectRoot = null;
33
+ /** 设置创作根(index.ts startServer 调用;测试隔离用传 null 重置) */
34
+ export function setProjectRoot(root) {
35
+ projectRoot = root;
36
+ }
37
+ /** books/ 子目录名(书架模式:创作根/books/<书名>/) */
38
+ export const BOOKS_DIR_NAME = "books";
39
+ /**
40
+ * 规范化并校验项目路径(决策 17,create/open 通用):
41
+ *
42
+ * 1. **必须是绝对路径**:endpoints.md 契约要求绝对路径;path.resolve 对相对输入会基于
43
+ * process.cwd() 折叠,语义歧义(cwd 变化结果即变),显式拒绝
44
+ * 2. **path.resolve 规范化**:折叠 `..` 与重复分隔符(防 `..` 逃逸——规范化后即为最终操作目录,
45
+ * 不再拼接用户输入)
46
+ * 3. **realpath 一致性校验**:解析后的真实路径(符号链接展开)必须与规范化路径一致,否则拒绝——
47
+ * 校验语义(决策 17「防越权读写任意目录」):本应用单项目根模型下「允许范围」即规范化后的
48
+ * 路径本身,符号链接会把读写导向意外位置(链接指向任意目录即越权),故链接跳转一律拒绝
49
+ *
50
+ * 调用方约定:create 流程在调用前先 mkdirSync(目录可能尚不存在,realpath 需要目录存在);
51
+ * open 流程目录不存在时 realpath 抛错 → 同样 400。
52
+ *
53
+ * @param rawPath 用户提供的路径(绝对路径)
54
+ * @returns 规范化后的项目目录绝对路径
55
+ * @throws HttpError 400 INVALID_PROJECT_PATH
56
+ */
57
+ export function resolveProjectDir(rawPath) {
58
+ if (typeof rawPath !== "string" || rawPath.trim() === "") {
59
+ throw new HttpError(400, "INVALID_PROJECT_PATH", "path 不能为空");
60
+ }
61
+ if (!isAbsolute(rawPath)) {
62
+ throw new HttpError(400, "INVALID_PROJECT_PATH", "path 必须是绝对路径");
63
+ }
64
+ const resolved = resolve(rawPath);
65
+ let real;
66
+ try {
67
+ real = realpathSync(resolved);
68
+ }
69
+ catch {
70
+ throw new HttpError(400, "INVALID_PROJECT_PATH", `路径不存在或不可访问: ${resolved}`);
71
+ }
72
+ if (real !== resolved) {
73
+ throw new HttpError(400, "INVALID_PROJECT_PATH", `路径含符号链接跳转,已拒绝: ${resolved}`);
74
+ }
75
+ return resolved;
76
+ }
77
+ /** 项目路由(挂载于 /api/v1/project,index.ts) */
78
+ export const projectRoutes = new Hono();
79
+ // POST /api/v1/project/create —— 创建新项目(决策 8 初始化三文件)
80
+ projectRoutes.post("/create", async (c) => {
81
+ const raw = await c.req.json().catch(() => null); // 空 body / 非法 JSON → 校验失败
82
+ const parsed = projectCreateReqSchema.safeParse(raw);
83
+ if (!parsed.success) {
84
+ throw parsed.error; // → app.onError → 400 VALIDATION_ERROR(含 fields)
85
+ }
86
+ const { path: rawPath, config } = parsed.data;
87
+ // 路径校验(决策 17):绝对路径 + 规范化 + 链接检查;create 允许目录尚不存在——先建目录再校验
88
+ if (!isAbsolute(rawPath)) {
89
+ throw new HttpError(400, "INVALID_PROJECT_PATH", "path 必须是绝对路径");
90
+ }
91
+ const resolved = resolve(rawPath);
92
+ try {
93
+ mkdirSync(resolved, { recursive: true });
94
+ }
95
+ catch {
96
+ throw new HttpError(400, "INVALID_PROJECT_PATH", `目录创建失败: ${resolved}`);
97
+ }
98
+ const dir = resolveProjectDir(resolved);
99
+ // create 语义:新建项目;目录已是项目(含 project.json)→ 409 拒绝(不幂等复用,与 open 区分)
100
+ if (readProjectFile(dir) !== null) {
101
+ throw new HttpError(409, "PROJECT_ALREADY_EXISTS", `目录已是项目(含 project.json),请用 open 打开: ${dir}`);
102
+ }
103
+ // 初始化三文件(决策 8:project.json + data.db + outline.json,schema_version 同步写入,决策 13)
104
+ // initProject 内置:建目录(幂等)+ 三文件 + config 覆盖参数 + user_version=SCHEMA_VERSION
105
+ // (S1.1 审核建议:避免 brand-new 库在 open 时触发无意义删库重建并留下空库 data.db.v0.bak)
106
+ const project = initProject(dir, config);
107
+ // create 不打开项目(不设 currentProject;open 才打开),创建的 db 连接即刻释放
108
+ closeProject(project);
109
+ return c.json(ok({
110
+ id: project.config.id,
111
+ path: dir,
112
+ created: true,
113
+ }));
114
+ });
115
+ // POST /api/v1/project/open —— 打开已有项目(schema 版本检测 + 删库重建,决策 13 修订)
116
+ projectRoutes.post("/open", async (c) => {
117
+ const raw = await c.req.json().catch(() => null);
118
+ const parsed = projectOpenReqSchema.safeParse(raw);
119
+ if (!parsed.success) {
120
+ throw parsed.error;
121
+ }
122
+ const dir = resolveProjectDir(parsed.data.path); // 目录不存在 / 链接跳转 → 400
123
+ // open 必须包含 project.json(endpoints.md 第 65 行)
124
+ const config = readProjectFile(dir);
125
+ if (config === null) {
126
+ throw new HttpError(400, "INVALID_PROJECT_PATH", `目标目录不含 project.json,不是项目: ${dir}`);
127
+ }
128
+ // 打开顺序(oracle 审核建议 1):**先开新项目成功后再关旧的**——
129
+ // 若先 closeProject(prev) 再开新项目,openDatabase/ensureSchemaCompatible 抛错时
130
+ // currentProject 会悬挂指向连接已关闭的旧项目(后续请求 "connection not open" → 500)。
131
+ // 失败语义:open 失败 = 操作未生效——当前项目保持原样(连接仍有效、单例不变)。
132
+ // schema 版本检测(决策 13 修订 + endpoints.md 第 68-70 行):data.db user_version——
133
+ // 旧版本(< 当前)→ 删库重建(备份 data.db.v{n}.bak + outline.json.v{n}.bak、重置
134
+ // outline 空树、清空回收站);**未来版本(> 当前,E4)→ 拒绝打开 409
135
+ // PROJECT_VERSION_NEWER**(提示升级程序;不触发任何重建/备份,数据原封不动——
136
+ // 堵降级路径数据丢失,release-review §一 风险 2)。SchemaVersionError 由 db 包
137
+ // 抛出前已关闭本次打开的连接(无句柄泄漏)。
138
+ try {
139
+ const dbPath = join(dir, DATA_DB_FILE_NAME);
140
+ const db = openDatabase(dbPath);
141
+ let activeDb;
142
+ let result;
143
+ try {
144
+ const out = ensureSchemaCompatible(db, dir, dbPath);
145
+ activeDb = out.db;
146
+ result = out.result;
147
+ }
148
+ catch (err) {
149
+ // E4:未来版本拒绝(SchemaVersionError → 409 PROJECT_VERSION_NEWER,message 透传
150
+ // 「请升级程序后打开」;连接已由 db 包关闭,此处仅做错误码映射)
151
+ if (err instanceof SchemaVersionError) {
152
+ throw new HttpError(409, "PROJECT_VERSION_NEWER", err.message);
153
+ }
154
+ throw err;
155
+ }
156
+ // 新项目就绪,切换单例:释放旧项目连接(重建分支已关闭旧连接,closeProject 幂等;
157
+ // 匹配分支旧连接仍开,此处统一释放)
158
+ const prev = getCurrentProject();
159
+ if (prev !== null && prev.db !== activeDb) {
160
+ closeProject(prev);
161
+ }
162
+ const project = { root: dir, config, db: activeDb };
163
+ setCurrentProject(project);
164
+ // S4.2 启动一致性校验(决策 16 修订):打开项目即以大纲节点软删为准补标 DB 关联记录
165
+ //(先 DB 后 JSON 崩溃窗口的幽灵形态兜底,幂等;无软删节点不输出日志)
166
+ logSoftDeleteReconcile(reconcileSoftDelete(project));
167
+ // 响应:openResSchema 核心字段 + rebuilt 提示(端到端文档「向客户端提示已重建」,endpoints.md 第 69 行;
168
+ // rebuilt/fromVersion 为附加字段——shared 的 projectOpenResSchema 未含(shared 冻结约束),
169
+ // 此处不经 schema parse 直接构造,避免 zod 默认 strip 掉附加字段;建议 2 记录在案,契约修订时收敛)
170
+ // E5:有迁移路径的旧版本经前向迁移打开时附加 migrated:true(提示客户端已自动升级数据)
171
+ return c.json(ok({
172
+ id: config.id,
173
+ name: config.name,
174
+ language: config.language,
175
+ config: mapProjectFileToConfig(config),
176
+ ...(result.rebuilt ? { rebuilt: true, fromVersion: result.fromVersion } : {}),
177
+ ...(result.migrated ? { migrated: true, fromVersion: result.fromVersion } : {}),
178
+ }));
179
+ }
180
+ catch (err) {
181
+ // open 失败恢复(oracle 审核建议 1 兜底):正常情况下当前项目连接未被触碰(见上注释);
182
+ // 防御性检查——若旧项目连接已被关闭(如未来 ensureSchemaCompatible 失败路径提前关连接),
183
+ // 清空单例,后续业务请求走 requireCurrentProject → 409 NO_PROJECT_OPEN(语义化错误而非 500)
184
+ const prev = getCurrentProject();
185
+ if (prev !== null && !prev.db.open) {
186
+ setCurrentProject(null);
187
+ }
188
+ throw err;
189
+ }
190
+ });
191
+ // GET /api/v1/project/list —— 书架列表(S1.5):扫描创作根 books/ 下含 project.json 的书
192
+ projectRoutes.get("/list", (c) => {
193
+ if (projectRoot === null) {
194
+ throw new HttpError(500, "INTERNAL_ERROR", "创作根未初始化(startServer 未调用 setProjectRoot)");
195
+ }
196
+ const booksDir = join(projectRoot, BOOKS_DIR_NAME);
197
+ // books/ 不存在 → 空书架(不报错——新创作根首次启动的正常状态)
198
+ let entries;
199
+ try {
200
+ entries = readdirSync(booksDir, { withFileTypes: true });
201
+ }
202
+ catch (err) {
203
+ if (err.code === "ENOENT") {
204
+ return listResponse(c, { rootPath: projectRoot, books: [] });
205
+ }
206
+ throw err;
207
+ }
208
+ // 过滤:仅目录 + 含 project.json(readProjectFile 探测——损坏的 project.json 抛错向上传播,
209
+ // 与 open 语义一致:坏数据不静默吞)
210
+ const books = entries
211
+ .filter((e) => e.isDirectory())
212
+ .map((e) => ({ name: e.name, dir: join(booksDir, e.name) }))
213
+ .map((b) => {
214
+ const config = readProjectFile(b.dir);
215
+ return config === null ? null : { name: b.name, path: b.dir, updatedAt: config.updated_at };
216
+ })
217
+ .filter((b) => b !== null)
218
+ // 倒序:最近更新在前(ISO 8601 字符串字典序 = 时间序)
219
+ .sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
220
+ return listResponse(c, { rootPath: projectRoot, books });
221
+ });
222
+ /**
223
+ * list 响应统一出口:经 projectListResSchema 自检后返回(oracle 审核建议 1——
224
+ * endpoints.md 第 14 行「类型对应 types/api.ts 的 Zod schema」)。
225
+ *
226
+ * **ZodError 处理取舍**:errorHandler 对 ZodError 统一转 400 VALIDATION_ERROR(路由入参校验语义);
227
+ * 但此处 parse 的是**服务端自检**(构造的响应是否符合契约)——失败是服务端 bug 而非客户端
228
+ * 参数错误,直接让 ZodError 冒泡会误报 400。故 catch 后重新抛 HttpError(500, INTERNAL_ERROR),
229
+ * 保持「入参 ZodError → 400、服务端自检 ZodError → 500」的语义边界。
230
+ */
231
+ function listResponse(c, result) {
232
+ try {
233
+ return c.json(ok(projectListResSchema.parse(result)));
234
+ }
235
+ catch (err) {
236
+ throw new HttpError(500, "INTERNAL_ERROR", `list 响应不符合契约: ${err instanceof Error ? err.message : String(err)}`);
237
+ }
238
+ }
239
+ // POST /api/v1/project/close —— 关闭当前项目(释放数据库连接)
240
+ projectRoutes.post("/close", (c) => {
241
+ const project = getCurrentProject();
242
+ if (project !== null) {
243
+ closeProject(project);
244
+ setCurrentProject(null);
245
+ }
246
+ // 无当前项目时幂等返回 saved:true(close 语义是「确保已关闭」,非报错)
247
+ return c.json(ok({ saved: true }));
248
+ });
249
+ // GET /api/v1/project/export —— 导出当前项目三文件为 zip(E1,release-review §二)
250
+ //
251
+ // 响应:application/zip **二进制**(endpoints.md 通用约定显式例外,契约见 shared
252
+ // types/api.ts PROJECT_EXPORT_FILE_NAMES 注释);Content-Disposition attachment,
253
+ // 文件名 <书名>.zip(RFC 5987 filename* UTF-8 编码,中文书名安全)。
254
+ // 流程:requireCurrentProject(无项目 → 409,与 /config 一致)→ 三文件存在性防御
255
+ // (缺失任一 → 500:打开的项目三文件必然齐全,缺失即损坏,不导出半成品包)→
256
+ // wal_checkpoint(TRUNCATE) 把 WAL 合并回主文件(完整快照)→ zipSync 打包(条目名
257
+ // 保持数据文件原名,import 侧按固定名校验)。决策 17:key 存用户级配置,天然不入包。
258
+ projectRoutes.get("/export", (c) => {
259
+ const project = requireCurrentProject();
260
+ const dir = project.root;
261
+ // 三文件缺失任一 → 500(数据完整性推断:打开的项目三文件必然齐全,缺失即损坏)
262
+ const fileEntries = [
263
+ { name: PROJECT_FILE_NAME, path: join(dir, PROJECT_FILE_NAME) },
264
+ { name: OUTLINE_FILE_NAME, path: join(dir, OUTLINE_FILE_NAME) },
265
+ { name: DATA_DB_FILE_NAME, path: join(dir, DATA_DB_FILE_NAME) },
266
+ ];
267
+ for (const f of fileEntries) {
268
+ if (!existsSync(f.path)) {
269
+ throw new HttpError(500, "INTERNAL_ERROR", `项目数据文件缺失,无法导出: ${f.name}`);
270
+ }
271
+ }
272
+ // WAL checkpoint:合并 WAL 到主文件并截断——zip 内 data.db 为完整快照,无需附带 -wal/-shm
273
+ checkpointWal(project.db);
274
+ // fflate zipSync:对象形式(key = 条目文件名),key 插入序稳定(project.json → outline.json → data.db)
275
+ const zipData = zipSync({
276
+ [PROJECT_FILE_NAME]: readFileSync(join(dir, PROJECT_FILE_NAME)),
277
+ [OUTLINE_FILE_NAME]: readFileSync(join(dir, OUTLINE_FILE_NAME)),
278
+ [DATA_DB_FILE_NAME]: readFileSync(join(dir, DATA_DB_FILE_NAME)),
279
+ }, { level: 6 });
280
+ // Content-Disposition:ASCII fallback + RFC 5987 filename*(中文/空格书名编码安全)
281
+ const fileName = `${project.config.name}.zip`;
282
+ c.header("Content-Type", "application/zip");
283
+ c.header("Content-Disposition", `attachment; filename="book.zip"; filename*=UTF-8''${encodeURIComponent(fileName)}`);
284
+ return c.body(zipData);
285
+ });
286
+ // ============ 导入(E2) ============
287
+ /** 上传大小上限(50MB;ora-4 复核保留——zip 含 data.db,正常项目远小于此) */
288
+ const MAX_IMPORT_SIZE = 50 * 1024 * 1024;
289
+ /** 解压总字节预算(200MB,zip 炸弹防御:压缩比极高/超多条目的恶意包在解压阶段即中止) */
290
+ const MAX_UNZIP_BUDGET = 200 * 1024 * 1024;
291
+ /** 拼接 Uint8Array 分块(Unzip ondata 流累计) */
292
+ function concatChunks(chunks, totalSize) {
293
+ const out = new Uint8Array(totalSize);
294
+ let offset = 0;
295
+ for (const c of chunks) {
296
+ out.set(c, offset);
297
+ offset += c.length;
298
+ }
299
+ return out;
300
+ }
301
+ /**
302
+ * 带预算的 zip 解压(ora-4 zip 炸弹防御):
303
+ * fflate Unzip 流式解压(同步 push)——onfile 回调中**只解压白名单条目**(未知条目
304
+ * 仅记名不 start,省预算;白名单检查由调用方用 names 列表执行,语义与全量解压等价),
305
+ * ondata 累计解压总字节,超过预算抛 HttpError 中止(push 同步传播)。
306
+ * 返回 { entries(白名单条目解压结果), names(全部条目名)}。
307
+ */
308
+ function unzipWithBudget(zipData, budget) {
309
+ const entries = {};
310
+ const names = [];
311
+ let total = 0;
312
+ const unzipper = new Unzip((file) => {
313
+ names.push(file.name);
314
+ if (!PROJECT_EXPORT_FILE_NAMES.includes(file.name))
315
+ return; // 未知条目不解压
316
+ const chunks = [];
317
+ let size = 0;
318
+ file.ondata = (err, chunk, final) => {
319
+ if (err)
320
+ throw err;
321
+ size += chunk.length;
322
+ total += chunk.length;
323
+ if (total > budget) {
324
+ throw new HttpError(400, "VALIDATION_ERROR", `备份包解压超出预算(${budget} 字节上限,zip 炸弹防御)`);
325
+ }
326
+ chunks.push(chunk);
327
+ if (final)
328
+ entries[file.name] = concatChunks(chunks, size);
329
+ };
330
+ file.start();
331
+ });
332
+ // 关键(fflate API 契约):Unzip 默认仅注册 stored(0) 解码器——deflate(8) 压缩的
333
+ // zip(zipSync 默认)必须显式 register(UnzipInflate),否则 start() 报 unknown
334
+ // compression type(fflate README 明确要求)
335
+ unzipper.register(UnzipInflate);
336
+ unzipper.push(zipData, true);
337
+ // 防御:无 EOCD/零条目的输入 Unzip 流式**静默返回空**(unzipSync 会抛)——
338
+ // 显式判定坏包(非 zip 内容 → 「不是有效的项目备份包」而非「缺少文件」)
339
+ if (names.length === 0) {
340
+ throw new HttpError(400, "VALIDATION_ERROR", "不是有效的项目备份包(zip 解析失败)");
341
+ }
342
+ return { entries, names };
343
+ }
344
+ /** 书名校验(与 client Sidebar 新建项目同规则):禁路径分隔符/纯点/控制字符 */
345
+ function validateBookName(name) {
346
+ if (!name) {
347
+ throw new HttpError(400, "VALIDATION_ERROR", "缺少书名字段 name");
348
+ }
349
+ // 与 client 同规则(Sidebar.tsx L3):"/"、"\"、纯点(. / ..)、控制字符一律拒绝——
350
+ // name 直接拼 books/<name>/ 目录名,否则可逃出 books/(决策 17 防越权精神)
351
+ if (/[\\/]|^\.+$|[\u0000-\u001f]/.test(name)) {
352
+ throw new HttpError(400, "VALIDATION_ERROR", "书名不能包含 /、\\ 或为 . / ..");
353
+ }
354
+ }
355
+ /** project.json 顶层契约最小校验(E2 本项目内——shared 无文件形态 schema,不扩契约范围) */
356
+ function isValidProjectFile(parsed) {
357
+ if (typeof parsed !== "object" || parsed === null)
358
+ return false;
359
+ const p = parsed;
360
+ return typeof p.id === "string" && p.id.length > 0 && typeof p.name === "string" && typeof p.schema_version === "number";
361
+ }
362
+ /** outline.json 顶层契约校验(与 db 包 validateOutlineFile 同构:root 根 + schema_version 数字 + children 数组) */
363
+ function isValidOutlineFile(parsed) {
364
+ if (typeof parsed !== "object" || parsed === null)
365
+ return false;
366
+ const p = parsed;
367
+ return p.id === "root" && p.type === "root" && typeof p.schema_version === "number" && Array.isArray(p.children);
368
+ }
369
+ // POST /api/v1/project/import —— 导入备份 zip 为新书(E2,release-review §二)
370
+ //
371
+ // 流程(全部校验通过才搬入,校验失败不触碰 books/):
372
+ // content-length 预检(>50MB 快速拒绝,防超大请求先缓冲)→ multipart 解析
373
+ // (file=zip + name=书名)→ 书名校验(防路径逃逸)→ file.size 上限复核 →
374
+ // fflate Unzip 流式解压(**解压总字节预算 200MB**——zip 炸弹防御;未知条目仅记名
375
+ // 不解压;失败 400「不是有效的项目备份包」)→ **条目白名单**(只接受
376
+ // PROJECT_EXPORT_FILE_NAMES 三文件名,非白名单条目即损坏/恶意包,严格拒绝——
377
+ // 逐名比对天然防 zip 路径穿越)→ 三文件齐全 → project.json/outline.json 顶层契约
378
+ // → data.db 校验(**大小 > 0 → 打开成功 → user_version === SCHEMA_VERSION**;
379
+ // 0 字节/非 SQLite → 400 坏包;版本不匹配 409 SCHEMA_VERSION_MISMATCH 按相对版本
380
+ // 分流文案,拒绝导入不静默重建——与 open 的删库重建语义刻意区分)→ 目标
381
+ // books/<name>/ 冲突(409 PROJECT_ALREADY_EXISTS,与 create 同语义)→ mkdir + copy
382
+ // 原子搬入(任一失败清理半成品目录)→ 不打开(与 create 一致,前端刷新书架)。
383
+ // 校验在 mkdtemp 临时目录完成(finally 清理);目标目录由服务端拼 books/<name>/,
384
+ // 客户端不可指定路径(防越权)。
385
+ projectRoutes.post("/import", async (c) => {
386
+ if (projectRoot === null) {
387
+ throw new HttpError(500, "INTERNAL_ERROR", "创作根未初始化(startServer 未调用 setProjectRoot)");
388
+ }
389
+ // 0. 上传大小预检(ora-4:parseBody 缓冲前先查 content-length,快速拒绝超大请求,
390
+ // 避免先把几十上百 MB 读进内存;与 file.size 检查双保险——content-length 可缺失/不可信)
391
+ const contentLength = Number(c.req.header("content-length") ?? "0");
392
+ if (contentLength > MAX_IMPORT_SIZE) {
393
+ throw new HttpError(400, "VALIDATION_ERROR", `备份包超过大小上限(${MAX_IMPORT_SIZE / 1024 / 1024}MB)`);
394
+ }
395
+ // 1. multipart 解析(Hono 4 c.req.parseBody;非 multipart body → 解析失败 → 400)
396
+ const body = await c.req.parseBody().catch(() => null);
397
+ const name = typeof body?.name === "string" ? body.name.trim() : "";
398
+ validateBookName(name);
399
+ const file = body?.file;
400
+ if (!(file instanceof File)) {
401
+ throw new HttpError(400, "VALIDATION_ERROR", "缺少文件字段 file(zip 备份包)");
402
+ }
403
+ if (file.size > MAX_IMPORT_SIZE) {
404
+ throw new HttpError(400, "VALIDATION_ERROR", `备份包超过大小上限(${MAX_IMPORT_SIZE / 1024 / 1024}MB)`);
405
+ }
406
+ // 2. zip 解压(fflate Unzip 流式 + 解压总字节预算 200MB——ora-4 zip 炸弹防御;
407
+ // 未知条目不解压仅记名,白名单检查见步骤 3;解析失败/超预算 → 400)
408
+ let entries;
409
+ let entryNames;
410
+ try {
411
+ const result = unzipWithBudget(new Uint8Array(await file.arrayBuffer()), MAX_UNZIP_BUDGET);
412
+ entries = result.entries;
413
+ entryNames = result.names;
414
+ }
415
+ catch (err) {
416
+ if (err instanceof HttpError)
417
+ throw err;
418
+ throw new HttpError(400, "VALIDATION_ERROR", "不是有效的项目备份包(zip 解析失败)");
419
+ }
420
+ // 3. 条目白名单(严格拒绝):只接受三数据文件名——非白名单条目即损坏/恶意包,
421
+ // 逐名比对(条目名不含路径分隔符概念,白名单比对即防 zip 路径穿越)
422
+ const unknown = entryNames.filter((k) => !PROJECT_EXPORT_FILE_NAMES.includes(k));
423
+ if (unknown.length > 0) {
424
+ throw new HttpError(400, "VALIDATION_ERROR", `备份包含未知条目: ${unknown.join(", ")}(只接受 ${PROJECT_EXPORT_FILE_NAMES.join("/")})`);
425
+ }
426
+ const missing = PROJECT_EXPORT_FILE_NAMES.filter((f) => !(f in entries));
427
+ if (missing.length > 0) {
428
+ throw new HttpError(400, "VALIDATION_ERROR", `备份包缺少文件: ${missing.join(", ")}(非完整项目备份)`);
429
+ }
430
+ // 4. 临时目录校验(全部通过才搬入;finally 清理)
431
+ const tmpDir = mkdtempSync(join(tmpdir(), "ai-editor-import-"));
432
+ let projectId = "";
433
+ try {
434
+ for (const f of PROJECT_EXPORT_FILE_NAMES) {
435
+ writeFileSync(join(tmpDir, f), Buffer.from(entries[f]));
436
+ }
437
+ // project.json 顶层契约(JSON 可解析 + id/name/schema_version)
438
+ let projectConfig;
439
+ try {
440
+ projectConfig = JSON.parse(readFileSync(join(tmpDir, PROJECT_FILE_NAME), "utf8"));
441
+ }
442
+ catch {
443
+ throw new HttpError(400, "VALIDATION_ERROR", "备份包内 project.json 不是合法 JSON");
444
+ }
445
+ if (!isValidProjectFile(projectConfig)) {
446
+ throw new HttpError(400, "VALIDATION_ERROR", "备份包内 project.json 顶层契约不符(需 id/name/schema_version)");
447
+ }
448
+ projectId = projectConfig.id;
449
+ // outline.json 顶层契约({id:"root",type:"root",schema_version,children[]})
450
+ try {
451
+ const outline = JSON.parse(readFileSync(join(tmpDir, OUTLINE_FILE_NAME), "utf8"));
452
+ if (!isValidOutlineFile(outline)) {
453
+ throw new HttpError(400, "VALIDATION_ERROR", "备份包内 outline.json 顶层契约不符(需 {id:root,type:root,schema_version,children[]})");
454
+ }
455
+ }
456
+ catch (err) {
457
+ if (err instanceof HttpError)
458
+ throw err;
459
+ throw new HttpError(400, "VALIDATION_ERROR", "备份包内 outline.json 不是合法 JSON");
460
+ }
461
+ // data.db 校验顺序(ora-4):**文件大小 > 0 → 打开成功 → user_version**——
462
+ // 0 字节文件 SQLite 会当新库打开(user_version=0),必须先按坏包拒绝(400 而非 409);
463
+ // 非 SQLite 内容打开失败 → 400 坏包。
464
+ // user_version 判定(E5 决议,tasks.md E5 卡规格追加句):
465
+ // v === SCHEMA_VERSION → 接受(现状)
466
+ // v < SCHEMA_VERSION → **有迁移路径**(MIGRATIONS 存在连续链,open 时自动前向
467
+ // 迁移,数据保全完整)→ 接受;**无迁移路径** → 409(文案标注版本过旧无路径)
468
+ // v > SCHEMA_VERSION → 409(E4 语义:备份来自更高版本程序)
469
+ // 拒绝均不静默重建——导入是用户主动恢复备份,版本不兼容须明示。
470
+ const dbPath = join(tmpDir, DATA_DB_FILE_NAME);
471
+ if (statSync(dbPath).size === 0) {
472
+ throw new HttpError(400, "VALIDATION_ERROR", "备份包内 data.db 为空文件(损坏包)");
473
+ }
474
+ let db = null;
475
+ try {
476
+ db = openDatabase(dbPath);
477
+ const v = getUserVersion(db);
478
+ const acceptable = v === SCHEMA_VERSION || (v < SCHEMA_VERSION && hasMigrationPath(v, SCHEMA_VERSION));
479
+ if (!acceptable) {
480
+ const hint = v > SCHEMA_VERSION ? "备份来自更高版本程序" : "备份来自旧版本程序且无可用迁移路径";
481
+ throw new HttpError(409, "SCHEMA_VERSION_MISMATCH", `备份包 data.db 版本 (${v}) ${hint}(当前程序 ${SCHEMA_VERSION}),暂不支持导入`);
482
+ }
483
+ }
484
+ catch (err) {
485
+ if (err instanceof HttpError)
486
+ throw err;
487
+ throw new HttpError(400, "VALIDATION_ERROR", "备份包内 data.db 不是有效的 SQLite 数据库");
488
+ }
489
+ finally {
490
+ if (db !== null)
491
+ closeDatabase(db);
492
+ }
493
+ // 5. 目标目录冲突(books/<name>/ 已存在 → 409,与 create 的 PROJECT_ALREADY_EXISTS 同语义)
494
+ const bookDir = join(projectRoot, BOOKS_DIR_NAME, name);
495
+ if (existsSync(bookDir)) {
496
+ throw new HttpError(409, "PROJECT_ALREADY_EXISTS", `书架已存在同名书: ${name}`);
497
+ }
498
+ // 6. 原子搬入:mkdir(recursive 建 books/)+ 复制三文件;任一失败清理半成品目录
499
+ try {
500
+ mkdirSync(bookDir, { recursive: true });
501
+ for (const f of PROJECT_EXPORT_FILE_NAMES) {
502
+ copyFileSync(join(tmpDir, f), join(bookDir, f));
503
+ }
504
+ }
505
+ catch (err) {
506
+ rmSync(bookDir, { recursive: true, force: true }); // 不留下残缺书
507
+ throw err; // → errorHandler 500 INTERNAL_ERROR
508
+ }
509
+ return c.json(ok({ imported: true, id: projectId, path: bookDir, name }));
510
+ }
511
+ finally {
512
+ rmSync(tmpDir, { recursive: true, force: true });
513
+ }
514
+ });
515
+ // GET /api/v1/project/config —— 获取当前项目配置
516
+ projectRoutes.get("/config", (c) => {
517
+ const project = requireCurrentProject(); // 无当前项目 → 409 NO_PROJECT_OPEN
518
+ // 读内存 config(open 时载入、PUT 同步更新;单进程下与盘一致,data-flow.md 第 46 行)
519
+ return c.json(ok(mapProjectFileToConfig(project.config)));
520
+ });
521
+ // PUT /api/v1/project/config —— 更新当前项目配置
522
+ projectRoutes.put("/config", async (c) => {
523
+ const project = requireCurrentProject();
524
+ const raw = await c.req.json().catch(() => null);
525
+ const parsed = projectConfigUpdateReqSchema.safeParse(raw);
526
+ if (!parsed.success) {
527
+ throw parsed.error;
528
+ }
529
+ const { name, language, prompt, current_position } = parsed.data;
530
+ // current_position 校验:须指向存在的**非软删**大纲节点(endpoints.md 第 115 行);
531
+ // 400 + OUTLINE_NODE_NOT_FOUND(参数语义错误用 400,非资源访问 404)
532
+ if (current_position !== undefined && current_position !== null) {
533
+ const tree = readOutlineFile(project.root);
534
+ const node = findOutlineNode(tree, current_position);
535
+ if (node === undefined || node.deleted === true) {
536
+ throw new HttpError(400, "OUTLINE_NODE_NOT_FOUND", `current_position 指向的大纲节点不存在或已软删: ${current_position}`);
537
+ }
538
+ }
539
+ // 合并更新 + 刷新 updated_at(时间 ISO 8601 应用层写入,schema.md 第 16 行),写盘并同步内存
540
+ const next = {
541
+ ...project.config,
542
+ ...(name !== undefined ? { name } : {}),
543
+ ...(language !== undefined ? { language } : {}),
544
+ ...(prompt !== undefined ? { prompt } : {}),
545
+ ...(current_position !== undefined ? { current_position } : {}),
546
+ updated_at: nowIso(),
547
+ };
548
+ writeProjectFile(project.root, next);
549
+ project.config = next;
550
+ return c.json(ok({ updated: true }));
551
+ });
552
+ //# sourceMappingURL=project.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project.js","sourceRoot":"","sources":["../../src/routes/project.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { Hono } from "hono";
2
+ /** 提案路由(挂载于 /api/v1/proposal,index.ts) */
3
+ export declare const proposalRoutes: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
4
+ //# sourceMappingURL=proposal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proposal.d.ts","sourceRoot":"","sources":["../../src/routes/proposal.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAS5B,0CAA0C;AAC1C,eAAO,MAAM,cAAc,4EAAa,CAAC"}