@whispering233/ai-editor-server 0.0.21 → 0.0.29
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-0zEqGbv5.css +1 -0
- package/client-dist/assets/index-DMtHdMMq.js +521 -0
- package/client-dist/index.html +3 -3
- package/dist/backup.d.ts +78 -78
- package/dist/backup.d.ts.map +1 -1
- package/dist/backup.js +138 -139
- package/dist/backup.js.map +1 -1
- package/dist/consistency.d.ts +3 -3
- package/dist/consistency.d.ts.map +1 -1
- package/dist/consistency.js +4 -4
- package/dist/consistency.js.map +1 -1
- package/dist/debug.d.ts +1 -1
- package/dist/debug.js +7 -7
- package/dist/debug.js.map +1 -1
- package/dist/index.d.ts +9 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +37 -37
- package/dist/index.js.map +1 -1
- package/dist/middleware/error.d.ts +4 -4
- package/dist/middleware/error.d.ts.map +1 -1
- package/dist/middleware/error.js +7 -9
- package/dist/middleware/error.js.map +1 -1
- package/dist/middleware/project.d.ts +9 -9
- package/dist/middleware/project.d.ts.map +1 -1
- package/dist/middleware/project.js +23 -23
- package/dist/middleware/project.js.map +1 -1
- package/dist/reference-files.d.ts +12 -12
- package/dist/reference-files.d.ts.map +1 -1
- package/dist/reference-files.js +20 -21
- package/dist/reference-files.js.map +1 -1
- package/dist/routes/chat.d.ts +18 -18
- package/dist/routes/chat.d.ts.map +1 -1
- package/dist/routes/chat.js +70 -83
- package/dist/routes/chat.js.map +1 -1
- package/dist/routes/delta.d.ts.map +1 -1
- package/dist/routes/delta.js +27 -28
- package/dist/routes/delta.js.map +1 -1
- package/dist/routes/entity.d.ts.map +1 -1
- package/dist/routes/entity.js +44 -45
- package/dist/routes/entity.js.map +1 -1
- package/dist/routes/names.d.ts.map +1 -1
- package/dist/routes/names.js +8 -9
- package/dist/routes/names.js.map +1 -1
- package/dist/routes/outline.d.ts +1 -1
- package/dist/routes/outline.d.ts.map +1 -1
- package/dist/routes/outline.js +23 -24
- package/dist/routes/outline.js.map +1 -1
- package/dist/routes/project.d.ts +6 -6
- package/dist/routes/project.d.ts.map +1 -1
- package/dist/routes/project.js +91 -95
- package/dist/routes/project.js.map +1 -1
- package/dist/routes/proposal.d.ts.map +1 -1
- package/dist/routes/proposal.js +40 -42
- package/dist/routes/proposal.js.map +1 -1
- package/dist/routes/reference.d.ts.map +1 -1
- package/dist/routes/reference.js +1 -2
- package/dist/routes/reference.js.map +1 -1
- package/dist/routes/relation.d.ts.map +1 -1
- package/dist/routes/relation.js +14 -15
- package/dist/routes/relation.js.map +1 -1
- package/dist/routes/settings.d.ts +35 -17
- package/dist/routes/settings.d.ts.map +1 -1
- package/dist/routes/settings.js +157 -52
- package/dist/routes/settings.js.map +1 -1
- package/dist/routes/trash.d.ts.map +1 -1
- package/dist/routes/trash.js +15 -16
- package/dist/routes/trash.js.map +1 -1
- package/package.json +7 -7
- package/client-dist/assets/index-B51Kcb1K.css +0 -1
- package/client-dist/assets/index-zgl3whdc.js +0 -104
package/dist/routes/settings.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
// 设置路由(S1.3):GET/PUT /api/v1/settings/llm
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// ~/.ai-editor/config.json(HOME
|
|
1
|
+
// 设置路由(S1.3 + 批次十六多 provider):GET/PUT /api/v1/settings/llm
|
|
2
|
+
// 关键约束:各 provider key 只走三级解析链(不入项目文件):
|
|
3
|
+
// ① 环境变量(deepseek → DEEPSEEK_API_KEY;opencode-go → OPENCODE_API_KEY)
|
|
4
|
+
// ② 用户级配置 ~/.ai-editor/config.json api_keys[<provider>](HOME 可覆盖——测试隔离依赖;
|
|
5
|
+
// v1 旧 api_key 字段仅对 deepseek 生效)
|
|
6
|
+
// ③ pi-agent 配置 ~/.pi/agent/auth.json(**只读兜底**:条目 type=api_key 且 key 非空才生效,
|
|
7
|
+
// 文件不存在/非法/无该项 → 跳过;绝不写回)
|
|
8
|
+
// 模型解析不变式:model 属于激活 provider 目录;撞名模型(deepseek-v4-flash/pro 两家都有)
|
|
9
|
+
// 以 provider 消歧;跨 provider 激活由 PUT 携带 provider+model 成对完成(见 PUT 校验)。
|
|
5
10
|
import { mkdirSync, readFileSync } from "node:fs";
|
|
6
11
|
import { homedir } from "node:os";
|
|
7
12
|
import { dirname, join } from "node:path";
|
|
@@ -9,25 +14,37 @@ import { Hono } from "hono";
|
|
|
9
14
|
import { maskApiKey } from "@whispering233/ai-editor-shared";
|
|
10
15
|
import { writeJsonAtomic } from "@whispering233/ai-editor-db";
|
|
11
16
|
import { settingsLlmGetResSchema, settingsLlmPutReqSchema, userConfigFileSchema, } from "@whispering233/ai-editor-shared/schemas";
|
|
12
|
-
import { getAvailableModels } from "@whispering233/ai-editor-llm";
|
|
13
|
-
import { ok } from "../middleware/error.js";
|
|
14
|
-
/** 默认模型名(
|
|
17
|
+
import { DEFAULT_PROVIDER, getAvailableModels, REGISTERED_PROVIDERS } from "@whispering233/ai-editor-llm";
|
|
18
|
+
import { HttpError, ok } from "../middleware/error.js";
|
|
19
|
+
/** 默认模型名( settings 端点;两 provider 目录均含该模型——兜底/初始态稳定) */
|
|
15
20
|
export const DEFAULT_MODEL = "deepseek-v4-flash";
|
|
16
|
-
/**
|
|
21
|
+
/** 环境变量(各 provider 解析链第 ① 级;测试经 process.env 注入) */
|
|
17
22
|
export const DEEPSEEK_API_KEY_ENV = "DEEPSEEK_API_KEY";
|
|
18
|
-
|
|
23
|
+
export const OPENCODE_API_KEY_ENV = "OPENCODE_API_KEY";
|
|
24
|
+
/** provider id → 环境变量名(注册表内 provider 必含;未知 provider 无 env 级) */
|
|
25
|
+
const ENV_VAR_OF = {
|
|
26
|
+
deepseek: DEEPSEEK_API_KEY_ENV,
|
|
27
|
+
"opencode-go": OPENCODE_API_KEY_ENV,
|
|
28
|
+
};
|
|
29
|
+
/** 用户级配置文件相对 HOME 的路径(不入项目文件) */
|
|
19
30
|
export const USER_CONFIG_RELATIVE_PATH = join(".ai-editor", "config.json");
|
|
31
|
+
/** pi-agent 凭据文件相对 HOME 的路径(只读兜底;opencode-go 订阅 key 通常在此) */
|
|
32
|
+
export const PI_AGENT_AUTH_RELATIVE_PATH = join(".pi", "agent", "auth.json");
|
|
20
33
|
/** 缺省思考强度(与 SharedConfig 缺省一致,读侧兜底) */
|
|
21
34
|
export const DEFAULT_THINKING_LEVEL = "high";
|
|
22
35
|
/** 用户级配置文件绝对路径(os.homedir() 读 $HOME,测试设 HOME 即可隔离) */
|
|
23
36
|
export function userConfigPath() {
|
|
24
37
|
return join(homedir(), USER_CONFIG_RELATIVE_PATH);
|
|
25
38
|
}
|
|
39
|
+
/** pi-agent auth.json 绝对路径(只读;测试设 HOME 即可隔离) */
|
|
40
|
+
export function piAgentAuthPath() {
|
|
41
|
+
return join(homedir(), PI_AGENT_AUTH_RELATIVE_PATH);
|
|
42
|
+
}
|
|
26
43
|
/**
|
|
27
44
|
* 读取用户级配置;文件不存在 / JSON 损坏 / schema 不合法 → 返回空配置(默认值语义,不抛错)。
|
|
28
|
-
*
|
|
29
|
-
* 未来版本追加字段不使整份配置失效);v0
|
|
30
|
-
*
|
|
45
|
+
* schema 正式化(shared userConfigFileSchema,非 strict 宽松读取——用户自有文件,
|
|
46
|
+
* 未来版本追加字段不使整份配置失效);v0/v1 旧格式与 v2 同读侧兼容,不迁移不写回
|
|
47
|
+
* (用户下次在设置页保存时自然落新格式)。
|
|
31
48
|
*/
|
|
32
49
|
export function getUserConfig() {
|
|
33
50
|
try {
|
|
@@ -39,74 +56,162 @@ export function getUserConfig() {
|
|
|
39
56
|
return {};
|
|
40
57
|
}
|
|
41
58
|
}
|
|
59
|
+
/** 激活 provider:config.provider 已注册才采用;否则缺省 deepseek(配置漂移兜底) */
|
|
60
|
+
export function effectiveProvider() {
|
|
61
|
+
const p = getUserConfig().provider;
|
|
62
|
+
return p !== undefined && REGISTERED_PROVIDERS.some((x) => x.id === p) ? p : DEFAULT_PROVIDER;
|
|
63
|
+
}
|
|
64
|
+
/** config 级 key:api_keys[provider](含空串 = 显式清除)> v1 旧 api_key(仅 deepseek 读侧兼容) */
|
|
65
|
+
function configKeyOf(cfg, provider) {
|
|
66
|
+
if (cfg.api_keys !== undefined && Object.prototype.hasOwnProperty.call(cfg.api_keys, provider)) {
|
|
67
|
+
return cfg.api_keys[provider] || null; // 空串 = 显式清除(不回落旧字段)
|
|
68
|
+
}
|
|
69
|
+
if (provider === "deepseek" && typeof cfg.api_key === "string" && cfg.api_key !== "")
|
|
70
|
+
return cfg.api_key;
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
/** pi-agent auth.json 只读兜底:条目 type=api_key 且 key 非空才生效;任何异常 → null */
|
|
74
|
+
function piAgentKeyOf(provider) {
|
|
75
|
+
try {
|
|
76
|
+
const obj = JSON.parse(readFileSync(piAgentAuthPath(), "utf8"));
|
|
77
|
+
const entry = obj?.[provider];
|
|
78
|
+
if (typeof entry !== "object" || entry === null)
|
|
79
|
+
return null;
|
|
80
|
+
const e = entry;
|
|
81
|
+
if (e.type === "api_key" && typeof e.key === "string" && e.key !== "")
|
|
82
|
+
return e.key;
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/** provider 展示名(错误文案/调试用);未知 provider → 原 id */
|
|
90
|
+
export function providerDisplayName(provider) {
|
|
91
|
+
return REGISTERED_PROVIDERS.find((p) => p.id === provider)?.displayName ?? provider;
|
|
92
|
+
}
|
|
93
|
+
/** provider 环境变量名(错误文案用) */
|
|
94
|
+
export function providerEnvVar(provider) {
|
|
95
|
+
return ENV_VAR_OF[provider];
|
|
96
|
+
}
|
|
42
97
|
/**
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
98
|
+
* 计算某 provider 的有效 key 与掩码:环境变量 > config.json > pi-agent auth.json(只读兜底)
|
|
99
|
+
* 三级都无 → null(apiKeySet=false,无掩码)。缺省 provider = deepseek(旧单 provider 语义)。
|
|
100
|
+
*/
|
|
101
|
+
export function effectiveApiKey(provider = DEFAULT_PROVIDER) {
|
|
102
|
+
const envKey = process.env[ENV_VAR_OF[provider] ?? ""] ?? "";
|
|
103
|
+
const fileKey = configKeyOf(getUserConfig(), provider) ?? "";
|
|
104
|
+
const ambientKey = piAgentKeyOf(provider) ?? "";
|
|
105
|
+
const key = envKey || fileKey || ambientKey || null;
|
|
106
|
+
return key === null ? { key, masked: undefined } : { key, masked: maskApiKey(key) };
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* 写入用户级配置(v2 合并语义:显式 undefined 字段不覆盖已有值)。
|
|
110
|
+
* - provider/model/thinking_level 顶层覆盖
|
|
111
|
+
* - api_keys 按键合并:提供即覆盖,空字符串 = 删除该键(清空 map 时整个字段移除)
|
|
112
|
+
* - 落盘 schema_version=2;v1 旧 api_key 字段保留不迁移(读侧兼容已覆盖)
|
|
113
|
+
* 原子写:复用 @whispering233/ai-editor-db 的 writeJsonAtomic(临时文件 wx 独占 +
|
|
114
|
+
* 文件 fsync + rename + 目录 fsync)。目标在 $HOME/.ai-editor/ 下,目录 fsync 在部分
|
|
115
|
+
* 平台/文件系统受限时由 db 实现静默忽略(主链路已保证文件内容完整)
|
|
48
116
|
*/
|
|
49
117
|
export function saveUserConfig(partial) {
|
|
50
118
|
const file = userConfigPath();
|
|
51
119
|
const next = { ...getUserConfig() };
|
|
120
|
+
if (partial.provider !== undefined)
|
|
121
|
+
next.provider = partial.provider;
|
|
52
122
|
if (partial.model !== undefined)
|
|
53
123
|
next.model = partial.model;
|
|
54
124
|
if (partial.thinking_level !== undefined)
|
|
55
125
|
next.thinking_level = partial.thinking_level;
|
|
56
|
-
if (partial.
|
|
57
|
-
next.
|
|
58
|
-
|
|
59
|
-
|
|
126
|
+
if (partial.api_keys !== undefined) {
|
|
127
|
+
const map = { ...(next.api_keys ?? {}) };
|
|
128
|
+
for (const [k, v] of Object.entries(partial.api_keys)) {
|
|
129
|
+
if (v === "")
|
|
130
|
+
delete map[k]; // 空字符串 = 清除该家已保存 key
|
|
131
|
+
else
|
|
132
|
+
map[k] = v;
|
|
133
|
+
}
|
|
134
|
+
if (Object.keys(map).length > 0)
|
|
135
|
+
next.api_keys = map;
|
|
136
|
+
else
|
|
137
|
+
delete next.api_keys;
|
|
60
138
|
}
|
|
61
|
-
//
|
|
62
|
-
next.schema_version =
|
|
139
|
+
// 保存时落新格式:schema_version=2(读侧兼容已保证 v0/v1 文件零破坏)
|
|
140
|
+
next.schema_version = 2;
|
|
63
141
|
mkdirSync(dirname(file), { recursive: true }); // writeJsonAtomic 不负责建目录
|
|
64
142
|
writeJsonAtomic(file, next);
|
|
65
143
|
return next;
|
|
66
144
|
}
|
|
67
|
-
/**
|
|
68
|
-
* 计算有效 key 与掩码:环境变量 > config.json(决策 17 来源优先级)
|
|
69
|
-
* 两者都没有 → null(apiKeySet=false,无掩码)
|
|
70
|
-
*/
|
|
71
|
-
export function effectiveApiKey() {
|
|
72
|
-
const envKey = process.env[DEEPSEEK_API_KEY_ENV] ?? "";
|
|
73
|
-
const fileKey = getUserConfig().api_key ?? "";
|
|
74
|
-
const key = envKey || fileKey || null; // 环境变量优先
|
|
75
|
-
return key === null ? { key, masked: undefined } : { key, masked: maskApiKey(key) };
|
|
76
|
-
}
|
|
77
145
|
/** 设置路由(挂载于 /api/v1/settings) */
|
|
78
146
|
export const settingsRoutes = new Hono();
|
|
79
|
-
// GET /api/v1/settings/llm —— 读取 LLM 配置(key
|
|
147
|
+
// GET /api/v1/settings/llm —— 读取 LLM 配置(key 不回传明文,仅掩码;全量 provider 目录 + 各家 key 状态)
|
|
80
148
|
settingsRoutes.get("/llm", (c) => {
|
|
81
|
-
const
|
|
149
|
+
const cfg = getUserConfig();
|
|
150
|
+
const provider = effectiveProvider();
|
|
82
151
|
const payload = settingsLlmGetResSchema.parse({
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
152
|
+
provider,
|
|
153
|
+
model: cfg.model ?? DEFAULT_MODEL,
|
|
154
|
+
thinkingLevel: cfg.thinking_level ?? DEFAULT_THINKING_LEVEL,
|
|
155
|
+
// 全量注册 provider:各家目录 + 有效 key 状态(前端下拉分组 + key 缺失整组禁用的依据)
|
|
156
|
+
providers: REGISTERED_PROVIDERS.map((p) => {
|
|
157
|
+
const { key, masked } = effectiveApiKey(p.id);
|
|
158
|
+
return {
|
|
159
|
+
id: p.id,
|
|
160
|
+
displayName: p.displayName,
|
|
161
|
+
apiKeySet: key !== null,
|
|
162
|
+
...(masked !== undefined ? { apiKeyMasked: masked } : {}),
|
|
163
|
+
models: getAvailableModels(p.id).map((m) => ({
|
|
164
|
+
id: m.id,
|
|
165
|
+
provider: m.provider,
|
|
166
|
+
displayName: m.displayName,
|
|
167
|
+
contextWindow: m.contextWindow,
|
|
168
|
+
maxTokens: m.maxTokens,
|
|
169
|
+
reasoning: m.reasoning,
|
|
170
|
+
})),
|
|
171
|
+
};
|
|
172
|
+
}),
|
|
96
173
|
});
|
|
97
174
|
return c.json(ok(payload));
|
|
98
175
|
});
|
|
99
|
-
// PUT /api/v1/settings/llm —— 更新 LLM 配置(写入 ~/.ai-editor/config.json
|
|
176
|
+
// PUT /api/v1/settings/llm —— 更新 LLM 配置(写入 ~/.ai-editor/config.json,绝不入项目文件)
|
|
177
|
+
// 校验不变式:最终 (provider, model) 必须满足 model ∈ provider 目录
|
|
178
|
+
// - provider+model 成对(跨 provider 激活,前端恒成对发送);model 不在该目录 → 400
|
|
179
|
+
// - 只给 model:唯一归属自动定 provider;撞名(deepseek-v4-flash/pro 两目录都有)歧义 → 400(须显式 provider)
|
|
180
|
+
// - 只给 provider:当前 model 不在新目录 → 400(须显式 model;防静默改用户选择)
|
|
100
181
|
settingsRoutes.put("/llm", async (c) => {
|
|
101
182
|
const raw = await c.req.json().catch(() => null); // 空 body / 非法 JSON → 校验失败
|
|
102
183
|
const parsed = settingsLlmPutReqSchema.safeParse(raw);
|
|
103
184
|
if (!parsed.success) {
|
|
104
185
|
throw parsed.error; // → app.onError → 400 VALIDATION_ERROR(含 fields)
|
|
105
186
|
}
|
|
187
|
+
const { provider, model, thinking_level, api_keys } = parsed.data;
|
|
188
|
+
const cfg = getUserConfig();
|
|
189
|
+
let targetProvider = provider ?? effectiveProvider();
|
|
190
|
+
const currentModel = cfg.model ?? DEFAULT_MODEL;
|
|
191
|
+
if (model !== undefined) {
|
|
192
|
+
if (provider === undefined) {
|
|
193
|
+
// 只给 model:归属唯一 provider(撞名歧义 → 400,须显式 provider)
|
|
194
|
+
const owners = REGISTERED_PROVIDERS.filter((p) => getAvailableModels(p.id).some((m) => m.id === model));
|
|
195
|
+
if (owners.length === 1)
|
|
196
|
+
targetProvider = owners[0].id;
|
|
197
|
+
else if (owners.length === 0)
|
|
198
|
+
throw new HttpError(400, "VALIDATION_ERROR", `模型不在任何已注册 provider 目录: ${model}`);
|
|
199
|
+
else
|
|
200
|
+
throw new HttpError(400, "VALIDATION_ERROR", `模型在多 provider 目录中存在(撞名),请显式指定 provider: ${owners.map((p) => p.id).join("/")}`);
|
|
201
|
+
}
|
|
202
|
+
if (!getAvailableModels(targetProvider).some((m) => m.id === model)) {
|
|
203
|
+
throw new HttpError(400, "VALIDATION_ERROR", `模型 ${model} 不在 provider ${providerDisplayName(targetProvider)} 目录`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
else if (provider !== undefined && !getAvailableModels(provider).some((m) => m.id === currentModel)) {
|
|
207
|
+
// 只给 provider:当前 model 必须仍在新目录(默认模型两目录都有,正常路径恒过)
|
|
208
|
+
throw new HttpError(400, "VALIDATION_ERROR", `当前模型 ${currentModel} 不在 provider ${providerDisplayName(provider)} 目录,请显式选择模型`);
|
|
209
|
+
}
|
|
106
210
|
saveUserConfig({
|
|
107
|
-
model:
|
|
108
|
-
|
|
109
|
-
|
|
211
|
+
provider: model !== undefined || provider !== undefined ? targetProvider : undefined,
|
|
212
|
+
model,
|
|
213
|
+
thinking_level,
|
|
214
|
+
api_keys,
|
|
110
215
|
});
|
|
111
216
|
return c.json(ok({ saved: true })); // settingsLlmPutResSchema 形状
|
|
112
217
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings.js","sourceRoot":"","sources":["../../src/routes/settings.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"settings.js","sourceRoot":"","sources":["../../src/routes/settings.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trash.d.ts","sourceRoot":"","sources":["../../src/routes/trash.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"trash.d.ts","sourceRoot":"","sources":["../../src/routes/trash.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAe5B,wCAAwC;AACxC,eAAO,MAAM,WAAW,4EAAa,CAAC"}
|
package/dist/routes/trash.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
// 回收站路由(S2.2 大纲侧 + S4.3 实体侧):GET /trash、restore 实体/节点、purge 实体/节点
|
|
2
2
|
//
|
|
3
|
-
// 契约来源:doc/api/endpoints.md 第 660-736 行、决策 12(软删/级联还原/物理清除)。
|
|
4
3
|
// 组织说明:大纲侧(S2.2)与实体侧(S4.3)同文件;relation/delta 级联 helper 在 db 包
|
|
5
4
|
// queries/trash.ts(S4.1 下沉);实体 restore/purge 为纯 DB 操作(无 outline.json 侧),
|
|
6
|
-
// 写序天然满足「先 DB 后 JSON
|
|
7
|
-
// restore 祖先链校验(大纲侧):存在软删祖先 → 409 OUTLINE_ANCESTOR_DELETED
|
|
5
|
+
// 写序天然满足「先 DB 后 JSON」。
|
|
6
|
+
// restore 祖先链校验(大纲侧):存在软删祖先 → 409 OUTLINE_ANCESTOR_DELETED。
|
|
8
7
|
// purge 语义拦截(两侧同构):未软删对象拒绝 purge——仅用于回收站清理。
|
|
9
8
|
import { Hono } from "hono";
|
|
10
9
|
import { cascadePurge, cascadeRestore, getOutlinePathIds, readOutlineFile } from "@whispering233/ai-editor-db";
|
|
@@ -17,7 +16,7 @@ import { parseTypeParam } from "./entity.js";
|
|
|
17
16
|
import { getReferenceRowAny, removeReferenceFile, restoreReferenceFromTrash, } from "../reference-files.js";
|
|
18
17
|
/** 回收站路由(挂载于 /api/v1/trash,index.ts) */
|
|
19
18
|
export const trashRoutes = new Hono();
|
|
20
|
-
// GET /api/v1/trash —— 回收站列表(大纲侧 +
|
|
19
|
+
// GET /api/v1/trash —— 回收站列表(大纲侧 + 实体侧,)
|
|
21
20
|
trashRoutes.get("/", (c) => {
|
|
22
21
|
const project = requireCurrentProject();
|
|
23
22
|
const nodes = listDeletedNodes(project.root).map((n) => ({
|
|
@@ -30,7 +29,7 @@ trashRoutes.get("/", (c) => {
|
|
|
30
29
|
return c.json(ok({ entities: listDeletedEntities(project.db), nodes }));
|
|
31
30
|
});
|
|
32
31
|
// POST /api/v1/trash/outline/:nodeId/restore —— 还原(级联还原子树 + 关联关系与 Delta)
|
|
33
|
-
//
|
|
32
|
+
// 写序:**先 DB 后 JSON**——先级联还原 relation/delta,再原子写 outline.json。
|
|
34
33
|
// 祖先链预校验必须在 DB 级联**之前**:409 拒绝的请求不产生任何副作用
|
|
35
34
|
// (restoreOutlineNode 内部的校验在 JSON 写前,但 DB 已先行——此处路由层先拦截)
|
|
36
35
|
trashRoutes.post("/outline/:nodeId/restore", (c) => {
|
|
@@ -38,7 +37,7 @@ trashRoutes.post("/outline/:nodeId/restore", (c) => {
|
|
|
38
37
|
const nodeId = c.req.param("nodeId");
|
|
39
38
|
const tree = readOutlineFile(project.root);
|
|
40
39
|
const subtreeIds = collectSubtreeIds(tree, nodeId); // 404 语义
|
|
41
|
-
// 0.
|
|
40
|
+
// 0. 祖先链预校验:存在软删祖先 → 409,且不产生任何 DB 副作用
|
|
42
41
|
const path = getOutlinePathIds(tree, nodeId);
|
|
43
42
|
for (const ancestorId of path.slice(1, -1)) {
|
|
44
43
|
const ancestor = findNodeById(tree, ancestorId);
|
|
@@ -46,7 +45,7 @@ trashRoutes.post("/outline/:nodeId/restore", (c) => {
|
|
|
46
45
|
throw new HttpError(409, "OUTLINE_ANCESTOR_DELETED", `存在软删祖先 ${ancestorId},请先还原祖先再还原本节点`);
|
|
47
46
|
}
|
|
48
47
|
}
|
|
49
|
-
// 1. DB
|
|
48
|
+
// 1. DB 级联还原(全部还原,不因另一端仍软删而跳过)
|
|
50
49
|
const { relations, deltas } = cascadeRestore(project.db, subtreeIds);
|
|
51
50
|
// 2. JSON 原子写(还原节点 + 递归子树;db 层校验为双保险)
|
|
52
51
|
let restoredChildren;
|
|
@@ -59,7 +58,7 @@ trashRoutes.post("/outline/:nodeId/restore", (c) => {
|
|
|
59
58
|
return c.json(ok({ restored: true, restoredChildren, restoredRelations: relations, restoredDeltas: deltas }));
|
|
60
59
|
});
|
|
61
60
|
// DELETE /api/v1/trash/outline/:nodeId —— 物理清除(purge,不可恢复;仅用于回收站清理)
|
|
62
|
-
//
|
|
61
|
+
// 写序:**先 DB 后 JSON**——先物理清除 relation/delta,再原子写 outline.json
|
|
63
62
|
trashRoutes.delete("/outline/:nodeId", (c) => {
|
|
64
63
|
const project = requireCurrentProject();
|
|
65
64
|
const nodeId = c.req.param("nodeId");
|
|
@@ -73,20 +72,20 @@ trashRoutes.delete("/outline/:nodeId", (c) => {
|
|
|
73
72
|
// 1. DB 物理清除关联关系与 Delta
|
|
74
73
|
cascadePurge(project.db, subtreeIds);
|
|
75
74
|
// 2. JSON 原子写(移除整棵子树;物理删除的极端崩溃窗口:DB 已清、JSON 未清,
|
|
76
|
-
//
|
|
77
|
-
//
|
|
75
|
+
// 不在 S4.2 一致性校验范围——其只兜软删联动,同步操作中途
|
|
76
|
+
// 崩溃概率极低,注释明示)
|
|
78
77
|
purgeOutlineNode(project.root, nodeId);
|
|
79
78
|
return c.json(ok({ purged: true }));
|
|
80
79
|
});
|
|
81
|
-
// POST /api/v1/trash/entity/:type/:id/restore ——
|
|
80
|
+
// POST /api/v1/trash/entity/:type/:id/restore —— 还原软删实体()
|
|
82
81
|
// 纯 DB 操作:自身 deleted_at 置 NULL + updated_at 刷新(S4.1 db 层),级联还原关联关系与 Delta
|
|
83
82
|
// (全部还原,不因另一端仍软删而跳过——可见性由查询层兜底,端点还原后自动可见)。
|
|
84
|
-
// db 层幂等:实体不存在、类型不匹配或未软删 → null → 404
|
|
83
|
+
// db 层幂等:实体不存在、类型不匹配或未软删 → null → 404()。
|
|
85
84
|
trashRoutes.post("/entity/:type/:id/restore", (c) => {
|
|
86
85
|
const project = requireCurrentProject();
|
|
87
86
|
const type = parseTypeParam(c.req.param("type"));
|
|
88
87
|
const id = c.req.param("id");
|
|
89
|
-
// reference file
|
|
88
|
+
// reference file 类:先移回文件(.trash/ → references/)再 DB 还原——
|
|
90
89
|
// 文件缺失(外部清理)不阻塞还原(仅还原索引,详情读取时 409 REFERENCE_FILE_MISSING);
|
|
91
90
|
// references/ 同名冲突 → 递增命名 + 更新索引 file_name(uniqueFileNameIn);
|
|
92
91
|
// DB 还原失败(罕见)→ 文件已回 references/ + 索引仍软删 → scan「软删索引 + 文件回归」还原自愈
|
|
@@ -106,7 +105,7 @@ trashRoutes.post("/entity/:type/:id/restore", (c) => {
|
|
|
106
105
|
}
|
|
107
106
|
return c.json(ok({ restored: true, restoredRelations: result.restoredRelations, restoredDeltas: result.restoredDeltas }));
|
|
108
107
|
});
|
|
109
|
-
// DELETE /api/v1/trash/entity/:type/:id —— 物理清除(purge
|
|
108
|
+
// DELETE /api/v1/trash/entity/:type/:id —— 物理清除(purge,;不可恢复,
|
|
110
109
|
// 仅用于回收站清理)。拦截顺序与 outline purge 同构:先 404(不存在)后 400(未软删)——
|
|
111
110
|
// 未软删对象拒绝,防误调把未进回收站的数据物理清掉(oracle 审核建议)。
|
|
112
111
|
trashRoutes.delete("/entity/:type/:id", (c) => {
|
|
@@ -114,7 +113,7 @@ trashRoutes.delete("/entity/:type/:id", (c) => {
|
|
|
114
113
|
const type = parseTypeParam(c.req.param("type"));
|
|
115
114
|
const id = c.req.param("id");
|
|
116
115
|
// 存在性 + 软删状态检查(原始 SQL:路由守卫,S4.1 purgeEntity 明确不校验「必须已软删」,
|
|
117
|
-
//
|
|
116
|
+
// outline purge 的 isSoftDeleted 同构——db 层只保证原子物理清除)
|
|
118
117
|
const row = project.db
|
|
119
118
|
.prepare("SELECT deleted_at FROM entities WHERE id = ? AND type = ?")
|
|
120
119
|
.get(id, type);
|
|
@@ -124,7 +123,7 @@ trashRoutes.delete("/entity/:type/:id", (c) => {
|
|
|
124
123
|
if (row.deleted_at === null) {
|
|
125
124
|
throw new HttpError(400, "VALIDATION_ERROR", `实体未软删,purge 仅用于回收站清理: ${id}`);
|
|
126
125
|
}
|
|
127
|
-
// reference file
|
|
126
|
+
// reference file 类:先物理删文件(references/ 与 .trash/ 都尝试)再 DB purge——
|
|
128
127
|
// 文件 = 真相源,删除是主操作;DB purge 失败(罕见)→ 残留软删索引(回收站可见可重试),
|
|
129
128
|
// 不会出现「文件残留被 scan 复活成新条目」的困惑
|
|
130
129
|
if (type === "reference") {
|
package/dist/routes/trash.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trash.js","sourceRoot":"","sources":["../../src/routes/trash.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,EAAE;AACF,
|
|
1
|
+
{"version":3,"file":"trash.js","sourceRoot":"","sources":["../../src/routes/trash.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@whispering233/ai-editor-server",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.29",
|
|
4
4
|
"license": "MIT",
|
|
5
|
-
"description": "Hono API 服务端(单进程:API + SPA
|
|
5
|
+
"description": "Hono API 服务端(单进程:API + SPA 静态托管)",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./dist/index.js",
|
|
8
8
|
"types": "./dist/index.d.ts",
|
|
@@ -31,11 +31,11 @@
|
|
|
31
31
|
"postpack": "node ../../scripts/restore-package-json.mjs"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@whispering233/ai-editor-agent": "0.0.
|
|
35
|
-
"@whispering233/ai-editor-db": "0.0.
|
|
36
|
-
"@whispering233/ai-editor-llm": "0.0.
|
|
37
|
-
"@whispering233/ai-editor-shared": "0.0.
|
|
38
|
-
"@whispering233/ai-editor-tools": "0.0.
|
|
34
|
+
"@whispering233/ai-editor-agent": "0.0.29",
|
|
35
|
+
"@whispering233/ai-editor-db": "0.0.29",
|
|
36
|
+
"@whispering233/ai-editor-llm": "0.0.29",
|
|
37
|
+
"@whispering233/ai-editor-shared": "0.0.29",
|
|
38
|
+
"@whispering233/ai-editor-tools": "0.0.29",
|
|
39
39
|
"@hono/node-server": "^2.0.12",
|
|
40
40
|
"fflate": "^0.8.3",
|
|
41
41
|
"hono": "^4.7.0",
|