diff --git a/src/api/markdown.rs b/src/api/markdown.rs index 92ff537..6bfba64 100644 --- a/src/api/markdown.rs +++ b/src/api/markdown.rs @@ -190,8 +190,8 @@ pub fn render_markdown_enhanced(md: &str) -> RenderedContent { } RenderedContent { - html: crate::utils::html_minify::minify_html(&clean_html(&html)), - toc_html: crate::utils::html_minify::minify_html(&toc_html), + html: clean_html(&html), + toc_html, } } diff --git a/src/bin/generate_highlight_css.rs b/src/bin/generate_highlight_css.rs index 7c94aee..46ca8b6 100644 --- a/src/bin/generate_highlight_css.rs +++ b/src/bin/generate_highlight_css.rs @@ -34,13 +34,9 @@ fn main() { let mocha_css = css_for_theme_with_class_style(&mocha, ClassStyle::Spaced) .expect("Failed to generate Mocha CSS"); - // 移除 syntect 生成的 /* ... */ 注释,减小最终 CSS 体积 - let latte_clean = strip_comments(&latte_css); - let mocha_clean = strip_comments(&mocha_css); - // 重写选择器:浅色主题无前缀,深色主题加 `.dark ` 前缀 - let latte_rewritten = rewrite_rules(&latte_clean, ".md-content pre code", ""); - let mocha_rewritten = rewrite_rules(&mocha_clean, ".md-content pre code", ".dark "); + let latte_rewritten = rewrite_rules(&latte_css, ".md-content pre code", ""); + let mocha_rewritten = rewrite_rules(&mocha_css, ".md-content pre code", ".dark "); let mut output = String::new(); output.push_str("/* Auto-generated by generate_highlight_css — DO NOT EDIT */\n"); @@ -49,7 +45,7 @@ fn main() { output.push_str("/* Catppuccin Mocha (dark) */\n"); output.push_str(&mocha_rewritten); - // 压缩最终 CSS:去掉注释、空白和换行 + // 压缩最终 CSS:注释、空白、换行统一在这一步处理(minify_css 会跳过 /* */) let output = minify_css(&output); // 确保 public/ 目录存在并写入生成的 CSS @@ -59,42 +55,6 @@ fn main() { println!("Generated public/highlight.css"); } -/// 去除 CSS 文本中的 C 风格 /* ... */ 注释。 -fn strip_comments(css: &str) -> String { - let mut result = String::with_capacity(css.len()); - let chars: Vec = css.chars().collect(); - let mut i = 0; - while i < chars.len() { - if i + 1 < chars.len() && chars[i] == '/' && chars[i + 1] == '*' { - // 遇到 /* 后跳过直到找到配对的 */ - while i + 1 < chars.len() && !(chars[i] == '*' && chars[i + 1] == '/') { - i += 1; - } - i += 2; - } else { - result.push(chars[i]); - i += 1; - } - } - result -} - -/// 将 syntect 生成的 CSS 选择器包装到指定的 base 选择器下,并可附加前缀。 -/// -/// syntect 原始输出形如: -/// .code { ... } -/// .kw { color: #xxx; } -/// .kw, .kt { color: #yyy; } -/// -/// 转换后变为: -/// .md-content pre code { ... } -/// .dark .md-content pre code .kw { ... } -/// -/// 参数: -/// - `css`: syntect 生成的原始 CSS; -/// - `base`: 作用域基础选择器,这里固定为 `.md-content pre code`; -/// - `prefix`: 主题前缀,浅色主题传空字符串,深色主题传 `.dark `。 -/// /// 压缩 CSS:移除注释、合并空白、删除选择器/属性周围的无用空格。 fn minify_css(css: &str) -> String { let mut out = String::with_capacity(css.len()); @@ -140,6 +100,21 @@ fn minify_css(css: &str) -> String { out.trim().to_string() } +/// 将 syntect 生成的 CSS 选择器包装到指定的 base 选择器下,并可附加前缀。 +/// +/// syntect 原始输出形如: +/// .code { ... } +/// .kw { color: #xxx; } +/// .kw, .kt { color: #yyy; } +/// +/// 转换后变为: +/// .md-content pre code { ... } +/// .dark .md-content pre code .kw { ... } +/// +/// 参数: +/// - `css`: syntect 生成的原始 CSS; +/// - `base`: 作用域基础选择器,这里固定为 `.md-content pre code`; +/// - `prefix`: 主题前缀,浅色主题传空字符串,深色主题传 `.dark `。 fn rewrite_rules(css: &str, base: &str, prefix: &str) -> String { let mut out = String::new(); let mut pos = 0; diff --git a/src/middleware/minify_html.rs b/src/middleware/minify_html.rs index 4bc3eff..1494c3e 100644 --- a/src/middleware/minify_html.rs +++ b/src/middleware/minify_html.rs @@ -1,8 +1,11 @@ //! SSR HTML 空白压缩中间件。 //! //! 对 Dioxus fullstack 返回的 `text/html` 响应做轻量 minify。 -//! 为了避免 SSR 增量渲染缓存命中后仍然重复 minify,中间件内部维护了一个按 URL -//! 缓存的内存缓存(容量有限、TTL 较短),minify 后的结果会直接复用。 +//! +//! 注意:这里**不**维护 per-URL 缓存。同一个 URL 在 admin 登录态与匿名态下 +//! 渲染出的 HTML 不同(例如是否带管理按钮),按 URL 缓存会导致跨用户内容 +//! 泄露。Dioxus 的增量渲染器(ISRG)本身已经按 URL 缓存了 SSR HTML, +//! 这里的 minify 只是对其输出做一次无损空白折叠,无需再叠加一层缓存。 #[cfg(feature = "server")] use axum::{ @@ -13,22 +16,9 @@ use axum::{ response::Response, }; use http_body_util::BodyExt; -use moka::future::Cache; -use std::time::Duration; - -/// 按 URL 缓存 minify 结果,避免 SSR 缓存命中后重复计算。 -static MINIFY_CACHE: std::sync::LazyLock> = - std::sync::LazyLock::new(|| { - Cache::builder() - .max_capacity(256) - .time_to_live(Duration::from_secs(300)) - .build() - }); /// Axum 中间件入口。 pub async fn layer(request: Request, next: Next) -> Response { - let uri = request.uri().clone(); - let path = uri.path().to_string(); let response = next.run(request).await; let is_html = response @@ -42,24 +32,6 @@ pub async fn layer(request: Request, next: Next) -> Response { return response; } - // 不缓存错误响应与后台管理页面,避免把错误页或敏感管理界面扩散。 - let status = response.status(); - let is_admin_or_auth = path.starts_with("/admin") || path == "/login" || path == "/register"; - let should_cache = status.is_success() && !is_admin_or_auth; - - // 缓存 key 必须包含完整 query string,避免不同参数共享同一份响应。 - let cache_key = format!( - "{}{}", - path, - uri.query().map(|q| format!("?{}", q)).unwrap_or_default() - ); - - if should_cache { - if let Some(cached) = MINIFY_CACHE.get(&cache_key).await { - return build_response(response, cached); - } - } - let (parts, body) = response.into_parts(); let bytes = match body.collect().await { Ok(collected) => collected.to_bytes(), @@ -74,11 +46,6 @@ pub async fn layer(request: Request, next: Next) -> Response { let original = String::from_utf8_lossy(&bytes); let minified = crate::utils::html_minify::minify_html(&original); - // 仅对成功且非后台页面写入缓存。 - if should_cache { - let _ = MINIFY_CACHE.insert(cache_key, minified.clone()).await; - } - let mut response = Response::builder() .status(parts.status) .header(header::CONTENT_TYPE, "text/html; charset=utf-8") @@ -94,20 +61,3 @@ pub async fn layer(request: Request, next: Next) -> Response { response } - -fn build_response(original: Response, body: String) -> Response { - let (parts, _body) = original.into_parts(); - let mut response = Response::builder() - .status(parts.status) - .header(header::CONTENT_TYPE, "text/html; charset=utf-8") - .body(Body::from(body.clone())) - .expect("failed to build cached response"); - - *response.headers_mut() = parts.headers; - response.headers_mut().remove(header::TRANSFER_ENCODING); - response - .headers_mut() - .insert(header::CONTENT_LENGTH, body.len().into()); - - response -} diff --git a/src/utils/html_minify.rs b/src/utils/html_minify.rs index d5a6b5e..3ff2e16 100644 --- a/src/utils/html_minify.rs +++ b/src/utils/html_minify.rs @@ -2,13 +2,28 @@ //! //! 仅服务端使用。对 HTML 片段做轻量 minify: //! - 合并标签之间连续空白为一个空格; -//! - 移除 HTML 注释; +//! - 移除普通 HTML 注释,但**保留 Dioxus hydration marker 注释**; //! - 保留 `
`、``、`