0.0.7:CalDAV 地址自动发现 + 人话错误提示 + 只读公开日历
背景:用户反馈(Nextcloud/内网)——DAVx5 用 CalDAV 地址能完整读取,换本应用
读不出日历;用"分享链接"绑定时提示服务器返回 405。
定位:不是协议问题,而是"填入的 URL 不是 CalDAV 端点"。
① 我们的发现=PROPFIND+Depth:1 只认 resourcetype 含 calendar 的集合;填 DAV 根
/remote.php/dav 只能看到 principals/calendars/addressbooks → "未发现日历本"。
② 分享链接(/apps/calendar/p/<token>、/s/<token>)是网页路由 → PROPFIND 405。
③ 全工程此前无 .well-known 自动发现(grep 无命中)→ 必须手填完整路径,
而 DAVx5 内置自动发现,故"DAVx5 能用、我们不能"。
实现(新增 common/DavDiscovery.ets):
1. .well-known/caldav 自动发现:207/200 直接用;301/302/307/308/401 读 Location
2. 智能补全:按 Nextcloud/ownCloud/Radicale/Baikal 等常见路径生成候选并逐个探测,
保留子目录前缀(填 https://h/nextcloud → /nextcloud/remote.php/dav/calendars/<u>/)
3. 分享链接识别:/apps/calendar/p/、/s/、/index.php/s/ 直接拦下并给改法
4. OPTIONS 能力探测:读 DAV/Allow 头,区分"不是 DAV 端点"与"服务端挡了方法"
5. 状态码人话化:405/401/403/404/跳转/网络失败各有可操作指引;
"未发现日历本"区分"地址差一层"(给出补全后的正确地址)与"确实为空"
6. 只读公开日历:识别 public-calendars 链接;只读由既有 calendarWritable 探测自动标记
接入三处:
- CalendarListPage.fetchCalendars / EditAccountPage:先 resolve() 定位端点,
命中则自动修正 serverUrl(并落库),失败则直接给人话原因
- AddAccountPage:连接前拦分享链接;probeServer 遇 405 用 OPTIONS 判定并给指引;
地址输入框提示同步更新
设计要点:发现顺序=原地址优先(保持既有行为、不破坏已能用的用户)→ .well-known
→ 常见候选;单个候选失败不抛错;ArkTS 静态方法内禁用 this → 统一用类名调用。
验证:编译通过;离线用例覆盖分类/完整性判定/候选路径(4 种输入形态)全部通过。
版本:AppScope/app.json5 → 0.0.7 / 700
文档:新增《上架更新说明-0.0.6到0.0.7.md》;官网 sf/uh 补功能说明与使用指引、
版本号与日期更新、404 重建,已部署并线上核验。
打包:assembleApp release → verify-app success、profile type=release、
app-distribution-type=app_gallery、包内版本 700/0.0.7。
This commit is contained in:
@@ -6,6 +6,8 @@ import { buffer } from '@kit.ArkTS';
|
||||
import url from '@ohos.url';
|
||||
import { BusinessError } from '@kit.BasicServicesKit';
|
||||
import { TYPE_CALDAV, TYPE_CARDDAV, TYPE_WEBDAV } from '../common/AccountStore';
|
||||
import { DavDiscovery, DavCapability } from '../common/DavDiscovery';
|
||||
import { LogUtil } from '../common/LogUtil';
|
||||
|
||||
@Entry
|
||||
@Component
|
||||
@@ -107,12 +109,30 @@ struct AddAccountPage {
|
||||
code = await this.sendOnce(serverUrl, http.RequestMethod.GET, authHeader);
|
||||
}
|
||||
if (code === 401) {
|
||||
this.statusMsg = '服务器拒绝凭据(401),请检查用户名密码';
|
||||
this.statusMsg = '服务器拒绝凭据(401):请检查用户名与密码。若服务器开启了二次验证,请使用「应用密码」';
|
||||
this.statusOk = false;
|
||||
return false;
|
||||
}
|
||||
// ⭐ 0.0.7:405 是"这里不是 DAV 端点"的典型信号 —— 直接给出可操作指引,
|
||||
// 而不是笼统地报一个状态码让用户去猜。
|
||||
if (code === 405) {
|
||||
const cap: DavCapability = await DavDiscovery.optionsCapability(serverUrl, authHeader);
|
||||
if (!cap.isDav) {
|
||||
this.statusMsg = '这个地址不是 CalDAV 端点(服务器不接受 WebDAV 方法,返回 405)。\n\n'
|
||||
+ '请填写「日历家目录」而不是 DAV 根或网页链接,例如:\n'
|
||||
+ 'https://你的域名/remote.php/dav/calendars/你的用户名/\n\n'
|
||||
+ '(也可以直接填域名,下一步会自动尝试发现)';
|
||||
this.statusOk = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (code >= 200 && code < 500) {
|
||||
this.statusMsg = '服务器连接成功';
|
||||
// ⭐ 即使可连,也顺手提示"地址看起来不完整",让用户提前知道下一步会自动补全
|
||||
if (DavDiscovery.looksIncomplete(serverUrl)) {
|
||||
this.statusMsg = '服务器连接成功(该地址看起来是服务器根路径,下一步会自动尝试定位日历家目录)';
|
||||
} else {
|
||||
this.statusMsg = '服务器连接成功';
|
||||
}
|
||||
this.statusOk = true;
|
||||
return true;
|
||||
}
|
||||
@@ -186,6 +206,17 @@ struct AddAccountPage {
|
||||
if (this.isLoading) {
|
||||
return;
|
||||
}
|
||||
// ⭐ 0.0.7:先识别「网页分享链接」——这类地址对 PROPFIND 必定 405,
|
||||
// 提前拦下并给出人话指引,避免用户白跑一趟只看到"服务器返回 405"。
|
||||
const kind: string = DavDiscovery.classify(targetUrl);
|
||||
const shareMsg: string = DavDiscovery.shareHint(kind);
|
||||
if (shareMsg !== '') {
|
||||
this.statusMsg = shareMsg;
|
||||
this.statusOk = false;
|
||||
LogUtil.write(`添加账号:识别为网页分享链接(${kind}),已拦截`);
|
||||
return;
|
||||
}
|
||||
|
||||
this.isLoading = true;
|
||||
this.statusMsg = '正在连接服务器…';
|
||||
this.statusOk = false;
|
||||
@@ -229,7 +260,7 @@ struct AddAccountPage {
|
||||
.width('100%')
|
||||
|
||||
Column({ space: 16 }) {
|
||||
this.formField('服务器地址', '例如:https://nas.example.com/caldav/', this.serverUrl,
|
||||
this.formField('服务器地址', '例如:https://nas.example.com/remote.php/dav/calendars/用户名/(也可只填域名,下一步会自动识别)', this.serverUrl,
|
||||
(value: string) => {
|
||||
this.serverUrl = value;
|
||||
}, false)
|
||||
|
||||
Reference in New Issue
Block a user