From 1b2fbdae4d49b3d7a3ab23df3bca6c2f4de8eaa9 Mon Sep 17 00:00:00 2001 From: voson Date: Sat, 11 Apr 2026 20:49:21 +0800 Subject: [PATCH] =?UTF-8?q?feat(secrets-mcp):=20/changelog=20=E9=A1=B5?= =?UTF-8?q?=EF=BC=88Markdown=20=E6=B8=B2=E6=9F=93=EF=BC=89=E3=80=81?= =?UTF-8?q?=E9=A6=96=E9=A1=B5=E4=B8=8E=20Dashboard=20=E5=85=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 CHANGELOG.md 构建嵌入、pulldown-cmark 渲染、路由 /changelog - 首页页脚与 MCP Dashboard 页脚提供变更记录链接;同步 README、AGENTS - 版本 secrets-mcp 0.5.24 --- AGENTS.md | 6 +- Cargo.lock | 43 ++++- README.md | 4 +- crates/secrets-mcp/CHANGELOG.md | 20 +++ crates/secrets-mcp/Cargo.toml | 3 +- crates/secrets-mcp/src/web/changelog.rs | 48 +++++ crates/secrets-mcp/src/web/mod.rs | 2 + crates/secrets-mcp/templates/changelog.html | 185 ++++++++++++++++++++ crates/secrets-mcp/templates/dashboard.html | 7 +- crates/secrets-mcp/templates/home.html | 10 +- 10 files changed, 316 insertions(+), 12 deletions(-) create mode 100644 crates/secrets-mcp/CHANGELOG.md create mode 100644 crates/secrets-mcp/src/web/changelog.rs create mode 100644 crates/secrets-mcp/templates/changelog.html diff --git a/AGENTS.md b/AGENTS.md index 5111d1c..7ff5d14 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -42,7 +42,7 @@ secrets/ Cargo.toml crates/ secrets-core/ # db / crypto / models / audit / service - secrets-mcp/ # rmcp tools、axum、OAuth、Dashboard + secrets-mcp/ # rmcp tools、axum、OAuth、Dashboard;CHANGELOG.md → /changelog scripts/ release-check.sh setup-gitea-actions.sh @@ -166,6 +166,10 @@ oauth_accounts ( | `secrets.type` | 密钥类型(调用方提供,默认 `text`) | `text`, `password`, `key` | | `secrets.encrypted` | 密文 | AES-GCM | +### Web 变更记录(`/changelog`) + +`crates/secrets-mcp/CHANGELOG.md` 在构建时嵌入,服务端以 **Markdown** 渲染为 HTML(`pulldown-cmark`)。**首页**(`/`)页脚与 **Dashboard**(`/dashboard`,MCP 配置页)页脚均提供「变更记录」链接;发版时随 `secrets-mcp` 版本更新该文件即可。 + ### Web JSON API 与会话 除页面路由使用的 `require_valid_user`(未登录或 `key_version` 与库不一致时重定向 `/login`)外,JSON API(`/api/...`)使用等价校验:会话中的 `key_version` 须与 `users.key_version` 一致,否则返回 **401** JSON,避免仅校验 `user_id` 时与页面行为不一致。 diff --git a/Cargo.lock b/Cargo.lock index 2c4fcc9..1b01bbd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -740,6 +740,15 @@ dependencies = [ "version_check", ] +[[package]] +name = "getopts" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe4fbac503b8d1f88e6676011885f34b7174f46e59956bba534ba83abded4df" +dependencies = [ + "unicode-width", +] + [[package]] name = "getrandom" version = "0.2.17" @@ -1578,6 +1587,25 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "pulldown-cmark" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c3a14896dfa883796f1cb410461aef38810ea05f2b2c33c5aded3649095fdad" +dependencies = [ + "bitflags", + "getopts", + "memchr", + "pulldown-cmark-escape", + "unicase", +] + +[[package]] +name = "pulldown-cmark-escape" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "007d8adb5ddab6f8e3f491ac63566a7d5002cc7ed73901f72057943fa71ae1ae" + [[package]] name = "quanta" version = "0.12.6" @@ -2065,7 +2093,7 @@ dependencies = [ [[package]] name = "secrets-mcp" -version = "0.5.21" +version = "0.5.24" dependencies = [ "anyhow", "askama", @@ -2075,6 +2103,7 @@ dependencies = [ "dotenvy", "governor", "http", + "pulldown-cmark", "rand 0.10.0", "reqwest", "rmcp", @@ -2985,6 +3014,12 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +[[package]] +name = "unicase" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" + [[package]] name = "unicode-bidi" version = "0.3.18" @@ -3012,6 +3047,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + [[package]] name = "unicode-xid" version = "0.2.6" diff --git a/README.md b/README.md index d04c7c5..ff42da5 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ SECRETS_DATABASE_SSL_ROOT_CERT=/etc/secrets/pg-ca.crt SECRETS_ENV=production ``` -- **Web**:`BASE_URL`(登录、Dashboard、设置密码短语、创建 API Key)。**条目**页 `/entries` 支持 folder 标签与条件筛选;表格列可在「显示列」中开关(名称与操作固定),**文件夹**列为可选列且默认显示。列可见性持久化见 [AGENTS.md](AGENTS.md)「Web 条目页表格列」。 +- **Web**:`BASE_URL`(登录、Dashboard、设置密码短语、创建 API Key)。**变更记录**页 **`/changelog`**:内容来自 `crates/secrets-mcp/CHANGELOG.md`(构建时嵌入并以 Markdown 渲染);首页页脚与 Dashboard(MCP)页脚均提供入口。**条目**页 `/entries` 支持 folder 标签与条件筛选;表格列可在「显示列」中开关(名称与操作固定),**文件夹**列为可选列且默认显示。列可见性持久化见 [AGENTS.md](AGENTS.md)「Web 条目页表格列」。 - **MCP**:Streamable HTTP 基址 `{BASE_URL}/mcp`,需 `Authorization: Bearer ` + `X-Encryption-Key: ` 请求头(读密文工具须带密钥)。 ## PostgreSQL TLS 加固 @@ -226,7 +226,7 @@ crates/secrets-core/ # db / crypto / models / audit / service src/ taxonomy.rs # SECRET_TYPE_OPTIONS(secret 字段类型下拉选项) service/ # 业务逻辑(add, search, update, delete, export, env_map 等) -crates/secrets-mcp/ # MCP HTTP、Web、OAuth、API Key +crates/secrets-mcp/ # MCP HTTP、Web、OAuth、API Key;CHANGELOG.md 嵌入 /changelog scripts/ release-check.sh # 发版前 fmt / clippy / test setup-gitea-actions.sh diff --git a/crates/secrets-mcp/CHANGELOG.md b/crates/secrets-mcp/CHANGELOG.md new file mode 100644 index 0000000..813f405 --- /dev/null +++ b/crates/secrets-mcp/CHANGELOG.md @@ -0,0 +1,20 @@ +本文档在构建时嵌入 Web 的 `/changelog` 页面,并由服务端渲染为 HTML。 + +## [0.5.24] - 2026-04-11 + +### Changed + +- 首页页脚将原「登录」入口改为「变更记录」(`/changelog`);顶部导航仍保留登录 / 进入控制台。 + +## [0.5.23] - 2026-04-11 + +### Added + +- Changelog 页使用 **Markdown** 渲染(`pulldown-cmark`:表格、~~删除线~~、任务列表等)。 + +## [0.5.22] - 2026-04-11 + +### Added + +- Dashboard(MCP)页脚版本旁增加「变更记录」链接,打开本变更说明页。 + diff --git a/crates/secrets-mcp/Cargo.toml b/crates/secrets-mcp/Cargo.toml index 9907705..6a22168 100644 --- a/crates/secrets-mcp/Cargo.toml +++ b/crates/secrets-mcp/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "secrets-mcp" -version = "0.5.21" +version = "0.5.24" edition.workspace = true [[bin]] @@ -45,3 +45,4 @@ urlencoding = "2" schemars = "1" http = "1" url = "2" +pulldown-cmark = "0.13.3" diff --git a/crates/secrets-mcp/src/web/changelog.rs b/crates/secrets-mcp/src/web/changelog.rs new file mode 100644 index 0000000..9da0608 --- /dev/null +++ b/crates/secrets-mcp/src/web/changelog.rs @@ -0,0 +1,48 @@ +use askama::Template; +use axum::{extract::State, http::StatusCode, response::Response}; +use pulldown_cmark::{Options, Parser, html}; + +use crate::AppState; + +use super::render_template; + +#[derive(Template)] +#[template(path = "changelog.html")] +pub(super) struct ChangelogTemplate { + pub base_url: String, + pub version: &'static str, + pub changelog_html: String, +} + +fn markdown_to_html(md: &str) -> String { + let mut opts = Options::empty(); + opts.insert(Options::ENABLE_TABLES); + opts.insert(Options::ENABLE_STRIKETHROUGH); + opts.insert(Options::ENABLE_TASKLISTS); + let parser = Parser::new_ext(md, opts); + let mut out = String::new(); + html::push_html(&mut out, parser); + out +} + +pub(super) async fn changelog_page(State(state): State) -> Result { + let md = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/CHANGELOG.md")); + render_template(ChangelogTemplate { + base_url: state.base_url.clone(), + version: env!("CARGO_PKG_VERSION"), + changelog_html: markdown_to_html(md), + }) +} + +#[cfg(test)] +mod tests { + use super::markdown_to_html; + + #[test] + fn markdown_renders_heading_and_list() { + let html = markdown_to_html("# Title\n\n- a\n"); + assert!(html.contains(" Router { get(assets::oauth_protected_resource_metadata), ) .route("/", get(auth::home_page)) + .route("/changelog", get(changelog::changelog_page)) .route("/login", get(auth::login_page)) .route("/auth/google", get(auth::auth_google)) .route("/auth/google/callback", get(auth::auth_google_callback)) diff --git a/crates/secrets-mcp/templates/changelog.html b/crates/secrets-mcp/templates/changelog.html new file mode 100644 index 0000000..9decbcd --- /dev/null +++ b/crates/secrets-mcp/templates/changelog.html @@ -0,0 +1,185 @@ + + + + + + + + 变更记录 — Secrets + + + +
+
+ secrets +
+ 控制台 +
+ + + +
+
+
+

变更记录

+
+ {{ changelog_html|safe }} +
+
+ 版本 {{ version }} +
+
+ + + diff --git a/crates/secrets-mcp/templates/dashboard.html b/crates/secrets-mcp/templates/dashboard.html index 3c7fa0a..bfa43b7 100644 --- a/crates/secrets-mcp/templates/dashboard.html +++ b/crates/secrets-mcp/templates/dashboard.html @@ -57,6 +57,8 @@ font-family: 'JetBrains Mono', monospace; margin-top: auto; } + .app-footer a { color: var(--accent); text-decoration: none; } + .app-footer a:hover { text-decoration: underline; } .card { background: #111827; border: 1px solid rgba(240,246,252,0.08); border-radius: 18px; padding: 20px; width: 100%; } .card-title { font-size: 22px; font-weight: 700; margin-bottom: 24px; color: #fff; } @@ -288,7 +290,7 @@ -
{{ version }}
+ @@ -379,6 +381,7 @@ const T = { regenFailed: '重置失败,请刷新页面重试。', ariaShowPw: '显示密码', ariaHidePw: '隐藏密码', + changelogLink: '变更记录', }, 'zh-TW': { navMcp: 'MCP', navEntries: '條目', navTrash: '回收站', navAudit: '審計', @@ -417,6 +420,7 @@ const T = { regenFailed: '重置失敗,請重新整理頁面再試。', ariaShowPw: '顯示密碼', ariaHidePw: '隱藏密碼', + changelogLink: '變更記錄', }, 'en': { navMcp: 'MCP', navEntries: 'Entries', navTrash: 'Trash', navAudit: 'Audit', @@ -455,6 +459,7 @@ const T = { regenFailed: 'Reset failed. Please refresh and try again.', ariaShowPw: 'Show password', ariaHidePw: 'Hide password', + changelogLink: 'Changelog', } }; diff --git a/crates/secrets-mcp/templates/home.html b/crates/secrets-mcp/templates/home.html index a5d34ee..3723edc 100644 --- a/crates/secrets-mcp/templates/home.html +++ b/crates/secrets-mcp/templates/home.html @@ -178,10 +178,8 @@ llms.txt · 源码仓库 - {% if !is_logged_in %} · - 登录 - {% endif %} + 变更记录