release(secrets-mcp): 0.5.4 — Web 分页修正与 hex 解码;批量删除上限;MCP @ 路径检测
Some checks failed
Secrets MCP — Build & Release / 检查 / 构建 / 发版 (push) Successful in 4m55s
Secrets MCP — Build & Release / 部署 secrets-mcp (push) Failing after 6s

This commit is contained in:
voson
2026-04-05 11:48:40 +08:00
parent 1860cce86c
commit 9d6ac5c13a
11 changed files with 92 additions and 32 deletions

View File

@@ -199,6 +199,15 @@ fn request_user_agent(headers: &HeaderMap) -> Option<String> {
.map(ToOwned::to_owned)
}
fn paginate(page: u32, total_count: i64, page_size: u32) -> (u32, u32, u32) {
let page_size = page_size.max(1);
let safe_total_count = u32::try_from(total_count.max(0)).unwrap_or(u32::MAX);
let total_pages = safe_total_count.div_ceil(page_size).max(1);
let current_page = page.max(1).min(total_pages);
let offset = (current_page - 1).saturating_mul(page_size);
(current_page, total_pages, offset)
}
// ── Routes ────────────────────────────────────────────────────────────────────
pub fn web_router() -> Router<AppState> {
@@ -605,8 +614,7 @@ async fn entries_page(
.filter(|s| !s.is_empty())
.map(|s| s.to_string());
let page = q.page.unwrap_or(1).max(1);
let offset = (page - 1) * ENTRIES_PAGE_LIMIT;
let params = SearchParams {
let count_params = SearchParams {
folder: folder_filter.as_deref(),
entry_type: type_filter.as_deref(),
name: None,
@@ -615,18 +623,22 @@ async fn entries_page(
query: None,
sort: "updated",
limit: ENTRIES_PAGE_LIMIT,
offset,
offset: 0,
user_id: Some(user_id),
};
let total_count = count_entries(&state.pool, &params)
let total_count = count_entries(&state.pool, &count_params)
.await
.inspect_err(|e| tracing::warn!(error = %e, "count_entries failed for web entries page"))
.unwrap_or(0);
let total_pages = (total_count as u32).div_ceil(ENTRIES_PAGE_LIMIT).max(1);
let current_page = page.min(total_pages);
let (current_page, total_pages, offset) = paginate(page, total_count, ENTRIES_PAGE_LIMIT);
let rows = list_entries(&state.pool, params).await.map_err(|e| {
let list_params = SearchParams {
offset,
..count_params
};
let rows = list_entries(&state.pool, list_params).await.map_err(|e| {
tracing::error!(error = %e, "failed to load entries list for web");
StatusCode::INTERNAL_SERVER_ERROR
})?;
@@ -846,11 +858,8 @@ async fn audit_page(
StatusCode::INTERNAL_SERVER_ERROR
})?;
let total_pages = (total_count as u32)
.div_ceil(AUDIT_PAGE_LIMIT as u32)
.max(1);
let current_page = page.min(total_pages);
let actual_offset = ((current_page - 1) as i64) * AUDIT_PAGE_LIMIT;
let (current_page, total_pages, offset) = paginate(page, total_count, AUDIT_PAGE_LIMIT as u32);
let actual_offset = i64::from(offset);
let rows = list_for_user(&state.pool, user_id, AUDIT_PAGE_LIMIT, actual_offset)
.await
@@ -1751,4 +1760,29 @@ mod tests {
assert!(matches!(request_ui_lang(&headers), UiLang::ZhTw));
}
#[test]
fn paginate_clamps_page_before_computing_offset() {
let (current_page, total_pages, offset) = paginate(100, 12, 10);
assert_eq!(current_page, 2);
assert_eq!(total_pages, 2);
assert_eq!(offset, 10);
}
#[test]
fn paginate_handles_large_page_without_overflow() {
let (current_page, total_pages, offset) = paginate(u32::MAX, 1, ENTRIES_PAGE_LIMIT);
assert_eq!(current_page, 1);
assert_eq!(total_pages, 1);
assert_eq!(offset, 0);
}
#[test]
fn paginate_saturates_large_total_count() {
let (_, total_pages, _) = paginate(1, i64::MAX, ENTRIES_PAGE_LIMIT);
assert_eq!(total_pages, u32::MAX.div_ceil(ENTRIES_PAGE_LIMIT));
}
}