feat: v2.3.0 设置内配置 Cloudflare OAuth 客户端 + 全列表批量删除
- 设置新增「Cloudflare 接入」tab:Client ID / Client Secret(Secret AES-GCM 加密入库,
只回显末 4 位),并展示可复制的授权回调地址;settings 优先、env 变量兜底,
保存后立即生效无需重新部署;DELETE /api/settings/cf-oauth 清除
- 四个资源列表(账号/域名/邮箱/收件箱)加复选框批量选择与批量删除
- 新增 POST /api/{accounts,domains,mailboxes,emails}/batch-delete:
账号软删+撤销凭据;域名/邮箱级联删除其下邮件;邮件连 R2 原文与附件一并清理
- 抽出 deleteEmailById / deleteEmailsByMailbox / revokeAccount 供单条与批量共用
- 点击复选框不再触发行点击(避免误开详情抽屉)
- 新增 tests/batch.test.ts(6 例)与 settings OAuth 凭据用例(2 例);ui-smoke 扩到 17 步
This commit is contained in:
@@ -1,11 +1,13 @@
|
||||
/**
|
||||
* 设置路由(v2.1.0):
|
||||
* GET /api/settings 读取:全局 Webhook / API Key 元信息 / 当前用户
|
||||
* PUT /api/settings 保存:全局 Webhook
|
||||
* 设置路由(v2.3.0):
|
||||
* GET /api/settings 读取:全局 Webhook / API Key 元信息 / Cloudflare OAuth 客户端 / 当前用户
|
||||
* PUT /api/settings 保存:全局 Webhook、Cloudflare OAuth 客户端
|
||||
* POST /api/settings/api-key 生成(轮换)单一 API/MCP Key,明文仅返回一次
|
||||
* DEL /api/settings/api-key 撤销并删除 API Key
|
||||
* DEL /api/settings/cf-oauth 清除后台保存的 Cloudflare OAuth 客户端凭据
|
||||
* PUT /api/settings/password 修改登录密码
|
||||
* API 与 MCP 共用同一 Key,凭据存 settings 表(不再单独建表)。
|
||||
* API 与 MCP 共用同一 Key,凭据存 settings 表(不再单独建表);
|
||||
* Cloudflare OAuth Client Secret 同样只存加密密文,接口只回 last4。
|
||||
*/
|
||||
|
||||
import { Hono } from "hono";
|
||||
@@ -19,6 +21,13 @@ import {
|
||||
verifyPassword,
|
||||
} from "../../../shared/src/index.ts";
|
||||
import { getSetting, keyHash, setSetting } from "../db.ts";
|
||||
import {
|
||||
CF_OAUTH_SETTING_KEYS,
|
||||
clearOAuthClient,
|
||||
oauthClientMeta,
|
||||
oauthRedirectUri,
|
||||
saveOAuthClient,
|
||||
} from "../services/cfapi.ts";
|
||||
import {
|
||||
getGlobalWebhookKey,
|
||||
normalizeWebhookHeaders,
|
||||
@@ -70,13 +79,17 @@ export function settingsRoutes(): App {
|
||||
username: user?.username ?? "",
|
||||
webhook,
|
||||
api_key: await apiKeyMeta(c.env),
|
||||
cf_oauth: await oauthClientMeta(c.env),
|
||||
// 供前端展示:需要在 Cloudflare Dashboard 登记的回调地址
|
||||
redirect_uri: oauthRedirectUri(c.env, new URL(c.req.url).origin),
|
||||
});
|
||||
});
|
||||
|
||||
/** 保存设置:全局 Webhook */
|
||||
/** 保存设置:全局 Webhook、Cloudflare OAuth 客户端 */
|
||||
app.put("/", async (c) => {
|
||||
const body = await readJson<{
|
||||
webhook?: { url?: string; headers?: unknown; key?: string };
|
||||
cf_oauth?: { client_id?: string; secret?: string };
|
||||
}>(c.req.raw);
|
||||
|
||||
if (body.webhook) {
|
||||
@@ -100,6 +113,23 @@ export function settingsRoutes(): App {
|
||||
});
|
||||
}
|
||||
|
||||
if (body.cf_oauth) {
|
||||
const clientId = (body.cf_oauth.client_id ?? "").trim();
|
||||
// secret:undefined 保留现有;空白串(含 "")清除;非空设置新 secret
|
||||
const rawSecret = typeof body.cf_oauth.secret === "string" ? body.cf_oauth.secret : undefined;
|
||||
if (rawSecret === undefined) {
|
||||
// 只改 client id:必须已存在 secret,否则保存后仍是未配置状态
|
||||
const current = await getSetting(c.env, CF_OAUTH_SETTING_KEYS.secretEnc);
|
||||
if (!current) return c.json({ error: "请填写 Client Secret" }, 400);
|
||||
if (clientId) await setSetting(c.env, CF_OAUTH_SETTING_KEYS.clientId, clientId);
|
||||
} else if (rawSecret.trim() === "") {
|
||||
await clearOAuthClient(c.env);
|
||||
} else {
|
||||
if (!clientId) return c.json({ error: "请填写 Client ID" }, 400);
|
||||
await saveOAuthClient(c.env, clientId, rawSecret.trim());
|
||||
}
|
||||
}
|
||||
|
||||
return c.json({ ok: true });
|
||||
});
|
||||
|
||||
@@ -122,6 +152,12 @@ export function settingsRoutes(): App {
|
||||
return c.json({ ok: true });
|
||||
});
|
||||
|
||||
/** 清除后台保存的 Cloudflare OAuth 客户端凭据(env 兜底仍可用) */
|
||||
app.delete("/cf-oauth", async (c) => {
|
||||
await clearOAuthClient(c.env);
|
||||
return c.json({ ok: true });
|
||||
});
|
||||
|
||||
/** 修改登录密码 */
|
||||
app.put("/password", async (c) => {
|
||||
const body = await readJson<{ old_password?: string; new_password?: string }>(c.req.raw);
|
||||
|
||||
Reference in New Issue
Block a user