feat: v2.1.0 界面与凭据改进 —— 单一 API/MCP Key、邮件 Markdown 正文、/inboxs 域名筛选、暗色主题
- 单一 Key:API 与 MCP 共用一个 wmh_ Key,凭据迁移至 settings 表(0004 删除 api_credentials) - Markdown:入库时 HTML→Markdown(mailhook 同思路),webhook payload 新增 markdown 字段 - /emails 路由改为 /inboxs 并支持域名筛选;设置按钮移入头像下拉 - 主题切换按钮(头像左侧,亮/暗持久化);设置模态仅 全局 Webhook/账号密码 两 tab 且高度限屏 - 列表操作列按钮加 SVG 图标;mailboxes 去 Webhook 列;最近邮件加收件人列
This commit is contained in:
@@ -1,16 +1,24 @@
|
||||
/**
|
||||
* 设置路由(v2.0.0):
|
||||
* GET /api/settings 读取:全局 Webhook / 当前用户
|
||||
* PUT /api/settings 保存:全局 Webhook
|
||||
* PUT /api/settings/password 修改登录密码
|
||||
* API/MCP Key 管理沿用 /api/keys(前端设置模态窗口内调用)。
|
||||
* 设置路由(v2.1.0):
|
||||
* GET /api/settings 读取:全局 Webhook / API Key 元信息 / 当前用户
|
||||
* PUT /api/settings 保存:全局 Webhook
|
||||
* POST /api/settings/api-key 生成(轮换)单一 API/MCP Key,明文仅返回一次
|
||||
* DEL /api/settings/api-key 撤销并删除 API Key
|
||||
* PUT /api/settings/password 修改登录密码
|
||||
* API 与 MCP 共用同一 Key,凭据存 settings 表(不再单独建表)。
|
||||
*/
|
||||
|
||||
import { Hono } from "hono";
|
||||
import type { Env, AppVars } from "../env.ts";
|
||||
import { requireSession } from "../middleware.ts";
|
||||
import { checkWebhookUrl, hashPassword, verifyPassword } from "../../../shared/src/index.ts";
|
||||
import { getSetting } from "../db.ts";
|
||||
import {
|
||||
checkWebhookUrl,
|
||||
generateWmailKey,
|
||||
hashPassword,
|
||||
isValidWmailKey,
|
||||
verifyPassword,
|
||||
} from "../../../shared/src/index.ts";
|
||||
import { getSetting, keyHash, setSetting } from "../db.ts";
|
||||
import {
|
||||
getGlobalWebhookKey,
|
||||
normalizeWebhookHeaders,
|
||||
@@ -20,6 +28,17 @@ import { nowIso, readJson } from "../util.ts";
|
||||
|
||||
type App = Hono<{ Bindings: Env; Variables: AppVars }>;
|
||||
|
||||
/** # 函数
|
||||
* 读取 API Key 元信息(只有 last4,无明文)
|
||||
*/
|
||||
async function apiKeyMeta(env: Env): Promise<{ has_key: boolean; key_last4: string; created_at: string | null }> {
|
||||
const [last4, createdAt] = await Promise.all([
|
||||
getSetting(env, "api_key_last4"),
|
||||
getSetting(env, "api_key_created_at"),
|
||||
]);
|
||||
return { has_key: Boolean(last4), key_last4: last4 ?? "", created_at: createdAt ?? null };
|
||||
}
|
||||
|
||||
/** # 函数
|
||||
* 装配设置路由(会话保护)
|
||||
*/
|
||||
@@ -50,6 +69,7 @@ export function settingsRoutes(): App {
|
||||
return c.json({
|
||||
username: user?.username ?? "",
|
||||
webhook,
|
||||
api_key: await apiKeyMeta(c.env),
|
||||
});
|
||||
});
|
||||
|
||||
@@ -83,6 +103,25 @@ export function settingsRoutes(): App {
|
||||
return c.json({ ok: true });
|
||||
});
|
||||
|
||||
/** 生成(轮换)API/MCP Key:明文仅本次返回;旧 Key 立即失效 */
|
||||
app.post("/api-key", async (c) => {
|
||||
const key = generateWmailKey();
|
||||
if (!isValidWmailKey(key)) return c.json({ error: "key generator broken" }, 500);
|
||||
const createdAt = nowIso();
|
||||
await setSetting(c.env, "api_key_hash", await keyHash(key));
|
||||
await setSetting(c.env, "api_key_last4", key.slice(-4));
|
||||
await setSetting(c.env, "api_key_created_at", createdAt);
|
||||
return c.json({ ok: true, key, created_at: createdAt }, 201);
|
||||
});
|
||||
|
||||
/** 撤销并删除 API Key */
|
||||
app.delete("/api-key", async (c) => {
|
||||
for (const k of ["api_key_hash", "api_key_last4", "api_key_created_at"]) {
|
||||
await c.env.DB.prepare("DELETE FROM settings WHERE key = ?").bind(k).run();
|
||||
}
|
||||
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