feat(oauth): 账号名改从 zone.account 取,去掉 account-settings.read 依赖
实测确认 GET /zones 每个 zone 自带 account {id,name},该接口只需 zone.read,
因此账号 id 与账号名都不必再调 GET /accounts(account-settings.read)。
顺带修掉 account_id 旧字段在当前响应里已为 null 导致的降级路径取不到账号 id。
- RealCFApi.discover 只列 /zones,移除 listAccounts 与 accountSource
- scope 候选集去掉 account-settings.read;设置页移除「含账号名权限」按钮
- 设置页 Scope 说明与 docs/02、README 同步(补 User Details Read 的区别说明)
- 85/85 测试通过(新增 zone 带 account / 不带 account 两种断言)
This commit is contained in:
+7
-20
@@ -21,10 +21,8 @@ export interface CfCall {
|
||||
|
||||
/** # 成员变量: 桩的返回数据配置 **/
|
||||
export interface CfMockOptions {
|
||||
/** /accounts 返回的账户列表 **/
|
||||
accounts?: { id: string; name: string }[];
|
||||
/** /zones 返回的域名列表 **/
|
||||
zones?: { id: string; name: string; account_id: string; status: string }[];
|
||||
/** /zones 返回的域名列表;每个 zone 自带 account(与真实 Cloudflare 响应一致) **/
|
||||
zones?: { id: string; name: string; account_id: string; status: string; account?: { id: string; name: string } }[];
|
||||
/** OAuth token 端点签发的 access token **/
|
||||
accessToken?: string;
|
||||
/** token 端点 HTTP 状态(测失败分支用) **/
|
||||
@@ -35,8 +33,6 @@ export interface CfMockOptions {
|
||||
scopesAllowed?: string[];
|
||||
/** 授权端点固定错误(走 Cloudflare 自己的 /oauth/error 页,不弹回回调地址) **/
|
||||
authorizeError?: string;
|
||||
/** /accounts 返回 403(模拟只授权了 zone 读、没有账号级读权限) */
|
||||
failAccounts?: boolean;
|
||||
}
|
||||
|
||||
/** # 成员变量: Cloudflare API 桩句柄 **/
|
||||
@@ -58,13 +54,14 @@ const API_BASE = "https://api.cloudflare.com/client/v4";
|
||||
|
||||
/** # 函数
|
||||
* 安装 Cloudflare API 桩:接管 globalThis.fetch,返回记录与恢复句柄
|
||||
* 参数: opts.accounts/zones 决定发现到的资源,tokenStatus 可模拟 token 兑换失败
|
||||
* 参数: opts.zones 决定发现到的资源,tokenStatus 可模拟 token 兑换失败
|
||||
*/
|
||||
export function installCloudflareMock(opts: CfMockOptions = {}): CfMock {
|
||||
const accounts = opts.accounts ?? [{ id: "cf-acct-real-1", name: "Real Cloudflare Account" }];
|
||||
const account = { id: "cf-acct-real-1", name: "Real Cloudflare Account" };
|
||||
// zone 自带 account(真实 Cloudflare 就在 zone 上附带),账号名因此无需账号级读权限
|
||||
const zones = opts.zones ?? [
|
||||
{ id: "zone-example", name: "example.com", account_id: accounts[0]?.id ?? "", status: "active" },
|
||||
{ id: "zone-demo", name: "demo.com", account_id: accounts[0]?.id ?? "", status: "active" },
|
||||
{ id: "zone-example", name: "example.com", account_id: account.id, status: "active", account },
|
||||
{ id: "zone-demo", name: "demo.com", account_id: account.id, status: "active", account },
|
||||
];
|
||||
const accessToken = opts.accessToken ?? "cf-oauth-access-token-xyz";
|
||||
const calls: CfCall[] = [];
|
||||
@@ -134,16 +131,6 @@ export function installCloudflareMock(opts: CfMockOptions = {}): CfMock {
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
if (url.startsWith(`${API_BASE}/accounts`)) {
|
||||
// 只拦账户列表本身;workers/scripts 上传同为 /accounts 前缀,不应受影响
|
||||
if (opts.failAccounts && url === `${API_BASE}/accounts`) {
|
||||
return new Response(
|
||||
JSON.stringify({ success: false, errors: [{ code: 9109, message: "Invalid access token" }], result: null }),
|
||||
{ status: 403, headers: { "Content-Type": "application/json" } },
|
||||
);
|
||||
}
|
||||
return json(accounts);
|
||||
}
|
||||
if (url.startsWith(`${API_BASE}/zones`)) {
|
||||
if (url.includes("/email/routing/rules/catch_all")) return json({ id: "catch-all-rule" });
|
||||
if (url.includes("/email/routing")) return json({ enabled: true });
|
||||
|
||||
Reference in New Issue
Block a user