同步容错:服务器删除日历本后 404 不再导致整个账号同步失败
- DavClient 新增 DavHttpError(带 HTTP 状态码),REPORT/PUT/DELETE 统一抛出 - 同步引擎对单本 404 跳过并自动从账号移除(先按 href 清行再迁移序号), 全部日历本均 404 时保留账号配置并给出明确提示 - 推送阶段单条失败不再中断整个账号(401 凭据失效仍立即抛出) - 编辑账号保存与自动移除都在序号迁移前先删被移除日历本的行, 修复行混入其它日历本 calKey 的老问题(实测「读书」988 行混进「轻舟」) - applyRemote 刷新行的 href:日历本被删除重建后集合 URL 变化, 旧行不再残留死地址(编辑推送 404 / 永久卡待推送) - pruneOrphanRows 追加 href 维度兜底清扫(跳过 dirty 行)
This commit is contained in:
@@ -36,6 +36,20 @@ export class DavCalendarDiscovery {
|
||||
entries: DavCalendarEntry[] = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 带 HTTP 状态码的 DAV 请求错误。
|
||||
* 同步引擎据此区分「404 日历本/资源在服务器上已不存在」(可降级处理:
|
||||
* 单本跳过、自动从账号移除)与其它网络/服务器故障(整个账号同步失败)。
|
||||
*/
|
||||
export class DavHttpError extends Error {
|
||||
readonly status: number;
|
||||
|
||||
constructor(status: number, message: string) {
|
||||
super(message);
|
||||
this.status = status;
|
||||
}
|
||||
}
|
||||
|
||||
export class DavClient {
|
||||
/**
|
||||
* PROPFIND 拉取某路径下所有集合的 calendar-color
|
||||
@@ -346,7 +360,7 @@ export class DavClient {
|
||||
});
|
||||
LogUtil.write(`HTTP REPORT(etag) ${href} → ${resp.responseCode}`);
|
||||
if (resp.responseCode < 200 || resp.responseCode >= 300) {
|
||||
throw new Error(`服务器返回状态码 ${resp.responseCode}`);
|
||||
throw new DavHttpError(resp.responseCode, `服务器返回状态码 ${resp.responseCode}`);
|
||||
}
|
||||
const xml: string = resp.result as string;
|
||||
const items: RemoteItem[] = [];
|
||||
@@ -403,11 +417,11 @@ export class DavClient {
|
||||
LogUtil.write(`HTTP REPORT(${compName}) ${href} → ${resp.responseCode}`);
|
||||
if (resp.responseCode === 401) {
|
||||
LogUtil.write(`HTTP REPORT(${compName}) 401 拒绝凭据`);
|
||||
throw new Error('服务器拒绝凭据(401)');
|
||||
throw new DavHttpError(401, '服务器拒绝凭据(401)');
|
||||
}
|
||||
if (resp.responseCode < 200 || resp.responseCode >= 300) {
|
||||
LogUtil.write(`HTTP REPORT(${compName}) 异常状态码 ${resp.responseCode}`);
|
||||
throw new Error(`服务器返回状态码 ${resp.responseCode}`);
|
||||
throw new DavHttpError(resp.responseCode, `服务器返回状态码 ${resp.responseCode}`);
|
||||
}
|
||||
const xml: string = resp.result as string;
|
||||
LogUtil.write(`HTTP REPORT(${compName}) 响应体 ${xml.length} 字符`);
|
||||
@@ -459,7 +473,7 @@ export class DavClient {
|
||||
console.info(`PUT ${url} 响应码: ${resp.responseCode}`);
|
||||
LogUtil.write(`HTTP PUT ${url} → ${resp.responseCode}`);
|
||||
if (resp.responseCode < 200 || resp.responseCode >= 300) {
|
||||
throw new Error(`推送失败,服务器返回 ${resp.responseCode}`);
|
||||
throw new DavHttpError(resp.responseCode, `推送失败,服务器返回 ${resp.responseCode}`);
|
||||
}
|
||||
const headers = resp.header as Record<string, string>;
|
||||
if (headers !== undefined && headers !== null) {
|
||||
@@ -489,7 +503,7 @@ export class DavClient {
|
||||
LogUtil.write(`HTTP DELETE ${url} → ${resp.responseCode}`);
|
||||
// 404 视为已删除,成功
|
||||
if ((resp.responseCode < 200 || resp.responseCode >= 300) && resp.responseCode !== 404) {
|
||||
throw new Error(`删除失败,服务器返回 ${resp.responseCode}`);
|
||||
throw new DavHttpError(resp.responseCode, `删除失败,服务器返回 ${resp.responseCode}`);
|
||||
}
|
||||
} finally {
|
||||
httpRequest.destroy();
|
||||
|
||||
Reference in New Issue
Block a user