@whispering233/ai-editor-server 0.0.5 → 0.0.7
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.
- package/client-dist/assets/index-CAmSH6A0.js +14 -0
- package/client-dist/index.html +1 -1
- package/dist/backup.d.ts +142 -0
- package/dist/backup.d.ts.map +1 -0
- package/dist/backup.js +629 -0
- package/dist/backup.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/middleware/project.d.ts +6 -1
- package/dist/middleware/project.d.ts.map +1 -1
- package/dist/middleware/project.js +16 -2
- package/dist/middleware/project.js.map +1 -1
- package/dist/routes/project.d.ts.map +1 -1
- package/dist/routes/project.js +209 -191
- package/dist/routes/project.js.map +1 -1
- package/package.json +7 -6
- package/client-dist/assets/index-D3Qif2tG.js +0 -14
package/client-dist/index.html
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
// localStorage 不可用(隐私模式等)时静默跳过,默认浅色
|
|
14
14
|
}
|
|
15
15
|
</script>
|
|
16
|
-
<script type="module" crossorigin src="/assets/index-
|
|
16
|
+
<script type="module" crossorigin src="/assets/index-CAmSH6A0.js"></script>
|
|
17
17
|
<link rel="stylesheet" crossorigin href="/assets/index-D2whPDhN.css">
|
|
18
18
|
</head>
|
|
19
19
|
<body>
|
package/dist/backup.d.ts
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import type { ProjectContext } from "./middleware/project.js";
|
|
2
|
+
/** 备份目录名(决策 27:项目目录内 .backups/,随书籍移动自然携带) */
|
|
3
|
+
export declare const BACKUPS_DIR_NAME = ".backups";
|
|
4
|
+
/** 备份文件信息(GET /backups 列表项与 POST /backup 响应,endpoints.md) */
|
|
5
|
+
export interface BackupFileInfo {
|
|
6
|
+
fileName: string;
|
|
7
|
+
size: number;
|
|
8
|
+
createdAt: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* 打包当前项目三文件为 zip(E1 export 同款管道):
|
|
12
|
+
* wal_checkpoint(TRUNCATE) 把 WAL 合并回主文件(zip 内 data.db 为完整快照,
|
|
13
|
+
* 无需附带 -wal/-shm)→ zipSync 打包(键序稳定:project.json → outline.json → data.db)。
|
|
14
|
+
* 三文件缺失任一 → 抛错(打开的项目三文件必然齐全,缺失即损坏,不导出半成品包)。
|
|
15
|
+
*/
|
|
16
|
+
export declare function createBackupZip(project: ProjectContext): Uint8Array<ArrayBuffer>;
|
|
17
|
+
/**
|
|
18
|
+
* 立即备份当前项目(手动触发 / 自动定时器 / restore 覆盖前快照共用):
|
|
19
|
+
* 打包 → 写入 .backups/<时间戳>.zip(同秒去重)→ 触发保留策略清理(失败不阻塞)。
|
|
20
|
+
* 写盘失败向上抛(errorHandler → 500 INTERNAL_ERROR,endpoints.md POST /backup 语义)。
|
|
21
|
+
*/
|
|
22
|
+
export declare function writeBackup(project: ProjectContext): BackupFileInfo;
|
|
23
|
+
/**
|
|
24
|
+
* 保留策略(决策 27):每项目保留最近 MAX_BACKUPS_PER_PROJECT 份,超出删除最旧。
|
|
25
|
+
* 排序依据 = 文件名时间戳(可解析的才参与;非法文件名不参与保留判定)。
|
|
26
|
+
* 清理失败不阻塞调用方(记日志即可,决策 27「清理失败不阻塞备份主流程」)。
|
|
27
|
+
*/
|
|
28
|
+
export declare function pruneBackups(backupsDir: string): void;
|
|
29
|
+
/** 备份列表(GET /backups):时间倒序(最新在前);.backups/ 不存在 → 空数组(不报错) */
|
|
30
|
+
export declare function listBackups(project: ProjectContext): BackupFileInfo[];
|
|
31
|
+
/**
|
|
32
|
+
* 备份包完整校验(自 E2 import 校验顺序 3-7 提取,restore 与 import 共用):
|
|
33
|
+
*
|
|
34
|
+
* 1. zip 解析(流式 + 解压总字节预算 200MB,zip 炸弹防御;失败 400「不是有效的项目备份包」)
|
|
35
|
+
* 2. 条目白名单(只接受 PROJECT_EXPORT_FILE_NAMES 三文件名,逐名比对天然防 zip 路径穿越)
|
|
36
|
+
* 3. 三文件齐全
|
|
37
|
+
* 4. 临时目录写入 → project.json / outline.json 顶层契约(JSON 损坏 / 契约不符 → 400)
|
|
38
|
+
* 5. data.db 校验(**大小 > 0 → 打开成功 → user_version**):
|
|
39
|
+
* - v === SCHEMA_VERSION → 接受(现状)
|
|
40
|
+
* - v < SCHEMA_VERSION → **有迁移路径** → 接受(open/restore 后自动前向迁移,E5);
|
|
41
|
+
* **无迁移路径** → 409(文案标注版本过旧无路径)
|
|
42
|
+
* - v > SCHEMA_VERSION → 409(E4 语义:备份来自更高版本程序)
|
|
43
|
+
* 拒绝均不静默重建——恢复备份须明示版本不兼容。
|
|
44
|
+
*
|
|
45
|
+
* 校验在 mkdtemp 临时目录完成(finally 清理,无论成败);通过后返回解压内容与
|
|
46
|
+
* project_id(调用方负责搬入/替换)。**校验通过前不触碰目标项目任何数据**(零触碰)。
|
|
47
|
+
*
|
|
48
|
+
* @throws HttpError 400(坏包/缺文件/未知条目/契约不符)/ 409 SCHEMA_VERSION_MISMATCH
|
|
49
|
+
*/
|
|
50
|
+
export declare function validateBackupPackage(zipData: Uint8Array): {
|
|
51
|
+
entries: Record<string, Uint8Array>;
|
|
52
|
+
projectId: string;
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* 自动备份单次检查(定时器 tick 核心,纯同步、可单测):
|
|
56
|
+
*
|
|
57
|
+
* 1. 频率判定:关闭(null/0/非枚举)→ 直接返回 false(决策 27:null/0 = 关闭;
|
|
58
|
+
* B2.1 疑问裁决 2:读侧非枚举值按关闭处理,不开启自动备份)
|
|
59
|
+
* 2. 变更判定:`.backups/` 为空(无上次备份时刻)→ 需要备份;否则三文件 mtime
|
|
60
|
+
* 均早于「上次备份时刻 + 容差」→ 跳过(无变更不产生垃圾备份,决策 27)
|
|
61
|
+
* 3. 有变更 → writeBackup(含保留策略清理)
|
|
62
|
+
*
|
|
63
|
+
* @returns 本次是否生成了备份
|
|
64
|
+
*/
|
|
65
|
+
export declare function maybeAutoBackup(project: ProjectContext): boolean;
|
|
66
|
+
/**
|
|
67
|
+
* 启动/重启自动备份调度(open/切换项目时调用;restore 后频率可能变化也调用):
|
|
68
|
+
* 按当前项目频率 setTimeout 链——每 tick 检查「有变更才备份」后按最新频率重新排程
|
|
69
|
+
* (tick 内重读 config,restore 改变频率无需显式通知)。
|
|
70
|
+
* 频率关闭(null/0/非枚举)→ 不排程(等价停止)。
|
|
71
|
+
*/
|
|
72
|
+
export declare function startAutoBackup(project: ProjectContext): void;
|
|
73
|
+
/** 停止自动备份调度(close 项目/服务关闭时调用;幂等) */
|
|
74
|
+
export declare function stopAutoBackup(): void;
|
|
75
|
+
/** 当前被定时器服务的项目(测试用;null = 未排程) */
|
|
76
|
+
export declare function getScheduledProject(): ProjectContext | null;
|
|
77
|
+
/**
|
|
78
|
+
* 用备份包内容替换目录三文件(restore 与 import 分流共用,B2.3 提取):
|
|
79
|
+
*
|
|
80
|
+
* - project.json:JSON 解析后按 opts 覆盖——`keepId`(决策 27:覆盖恢复以 project_id 为
|
|
81
|
+
* 唯一 key,换 id 即断连 chat_messages 会话历史——决策 18;备份包可能来自异项目,
|
|
82
|
+
* 防御性强制保留)与 `name`(「目录名 = 书名」不变式:import 覆盖归一为目录名、
|
|
83
|
+
* 新书导入同步为去重名);其余字段随备份替换。序列化走 2 空格缩进 + 尾换行
|
|
84
|
+
* (与 db 包 writeJsonAtomic 同款格式惯例)
|
|
85
|
+
* - outline.json / data.db:原样字节原子写
|
|
86
|
+
*
|
|
87
|
+
* 注意:本函数只管文件层,不含 data.db 连接管理(重连见 overwriteProjectFiles)。
|
|
88
|
+
*/
|
|
89
|
+
export declare function writeProjectFilesFromBackup(dir: string, entries: Record<string, Uint8Array>, opts?: {
|
|
90
|
+
keepId?: string;
|
|
91
|
+
name?: string;
|
|
92
|
+
snapshotFileName?: string;
|
|
93
|
+
}): void;
|
|
94
|
+
/**
|
|
95
|
+
* 覆盖管道(restore 与 import 覆盖当前打开的书共用,B2.3 从 restoreBackup 提取):
|
|
96
|
+
*
|
|
97
|
+
* 1. 释放当前 data.db 连接(替换 data.db 前必须;顺带清理陈旧 WAL/SHM 残留——
|
|
98
|
+
* 替换后新库不得复用旧 WAL)
|
|
99
|
+
* 2. writeProjectFilesFromBackup 原子替换三文件(保留当前项目 id;opts.name 归一)
|
|
100
|
+
* 3. 重连 + 版本对齐(v < 当前且有迁移路径 → 前向迁移,E5;v === 当前 → 原样。
|
|
101
|
+
* 校验阶段已拒绝 v > 当前与无路径旧版,此处不会触发重建/拒绝)
|
|
102
|
+
* 4. 同步内存 config(刚原子写入,readProjectFile 必非 null;损坏抛错由 catch 恢复连接)
|
|
103
|
+
* 5. 重启定时器(备份包内频率可能不同:5 → 60 等)
|
|
104
|
+
*
|
|
105
|
+
* 调用方职责:覆盖前自动快照 + 备份包校验(validateBackupPackage)。
|
|
106
|
+
* 失败时尝试恢复连接(原库可能未被替换或已替换为校验过的有效库),保证后续请求
|
|
107
|
+
* 不因悬挂连接报 500「connection not open」;**重连失败时连接悬挂**——由路由层
|
|
108
|
+
* (restore/import)在 catch 后检查 project.db.open 清空 currentProject 单例
|
|
109
|
+
* (backup 模块不依赖 middleware,避免运行时循环依赖)。
|
|
110
|
+
*/
|
|
111
|
+
export declare function overwriteProjectFiles(project: ProjectContext, entries: Record<string, Uint8Array>, opts?: {
|
|
112
|
+
name?: string;
|
|
113
|
+
snapshotFileName?: string;
|
|
114
|
+
}): void;
|
|
115
|
+
/**
|
|
116
|
+
* 为**未打开的书**生成覆盖前快照(import 覆盖书架其他书场景,决策 27「覆盖前自动快照」):
|
|
117
|
+
* 临时打开 data.db 连接走备份管道(wal_checkpoint 保证快照完整性),完成后关闭连接。
|
|
118
|
+
* 已打开的书走 writeBackup(project)(复用现有连接,见 import 路由分流)。
|
|
119
|
+
*/
|
|
120
|
+
export declare function snapshotBookDir(dir: string): BackupFileInfo;
|
|
121
|
+
/**
|
|
122
|
+
* 从备份恢复当前项目(POST /project/backup/restore,决策 27):
|
|
123
|
+
*
|
|
124
|
+
* 1. fileName 白名单校验:仅允许 `.backups/` 下 `<YYYYMMDD-HHmmss>.zip` 格式
|
|
125
|
+
* (shared parseBackupFileName,^$ 锚定天然拒绝路径分隔符/`..`,防路径穿越);
|
|
126
|
+
* 格式合法但文件不存在 → 404
|
|
127
|
+
* 2. **覆盖前自动快照**:当前三文件打包为快照存入 .backups/(复用备份管道,
|
|
128
|
+
* 就是普通备份文件,自然参与保留策略——后悔药,决策 27)
|
|
129
|
+
* 3. 备份包校验(validateBackupPackage:zip/白名单/契约/user_version 三态,
|
|
130
|
+
* E4/E5 语义,拒绝时数据零触碰——校验通过前不触碰任何数据文件)
|
|
131
|
+
* 4. **原子替换**:closeDatabase(释放当前连接)→ 三文件逐文件原子写
|
|
132
|
+
* (临时文件 + rename;data.db 顺带清理 -wal/-shm 残留)→ 重新 openDatabase
|
|
133
|
+
* + ensureSchemaCompatible(校验阶段已保证 v ≤ 当前且有迁移路径或匹配——
|
|
134
|
+
* v < 有路径时此处前向迁移,E5)→ project.config 同步新 project.json
|
|
135
|
+
* 5. 项目 id 保留(当前项目引用不变,决策 27);重启定时器(频率可能随备份变化)
|
|
136
|
+
*
|
|
137
|
+
* @throws HttpError 400(文件名非法/坏包)、404(备份不存在)、409 SCHEMA_VERSION_MISMATCH
|
|
138
|
+
*/
|
|
139
|
+
export declare function restoreBackup(project: ProjectContext, fileName: string): {
|
|
140
|
+
snapshot: Pick<BackupFileInfo, "fileName" | "createdAt">;
|
|
141
|
+
};
|
|
142
|
+
//# sourceMappingURL=backup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backup.d.ts","sourceRoot":"","sources":["../src/backup.ts"],"names":[],"mappings":"AAsCA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAE9D,6CAA6C;AAC7C,eAAO,MAAM,gBAAgB,aAAa,CAAC;AAe3C,6DAA6D;AAC7D,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAID;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,cAAc,GAAG,UAAU,CAAC,WAAW,CAAC,CAWhF;AAkBD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,cAAc,GAAG,cAAc,CAQnE;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAkBrD;AAED,6DAA6D;AAC7D,wBAAgB,WAAW,CAAC,OAAO,EAAE,cAAc,GAAG,cAAc,EAAE,CAoBrE;AA0ED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,UAAU,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAsFrH;AA4CD;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAOhE;AASD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAI7D;AAED,oCAAoC;AACpC,wBAAgB,cAAc,IAAI,IAAI,CAMrC;AAmBD,kCAAkC;AAClC,wBAAgB,mBAAmB,IAAI,cAAc,GAAG,IAAI,CAE3D;AAMD;;;;;;;;;;;GAWG;AACH,wBAAgB,2BAA2B,CACzC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,EACnC,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAO,GACvE,IAAI,CA6BN;AAED;;;;;;;;;;;;;;;;IAgBI;AACJ,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,EACnC,IAAI,GAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAO,GACtD,IAAI,CA+CN;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CAW3D;AAmCD;;;;;;;;;;;;;;;;;IAiBI;AACJ,wBAAgB,aAAa,CAAC,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,GAAG;IAAE,QAAQ,EAAE,IAAI,CAAC,cAAc,EAAE,UAAU,GAAG,WAAW,CAAC,CAAA;CAAE,CAkCrI"}
|