Some checks failed
BotServer CI / build (push) Failing after 1m34s
Split 20+ files over 1000 lines into focused subdirectories for better maintainability and code organization. All changes maintain backward compatibility through re-export wrappers. Major splits: - attendance/llm_assist.rs (2074→7 modules) - basic/keywords/face_api.rs → face_api/ (7 modules) - basic/keywords/file_operations.rs → file_ops/ (8 modules) - basic/keywords/hear_talk.rs → hearing/ (6 modules) - channels/wechat.rs → wechat/ (10 modules) - channels/youtube.rs → youtube/ (5 modules) - contacts/mod.rs → contacts_api/ (6 modules) - core/bootstrap/mod.rs → bootstrap/ (5 modules) - core/shared/admin.rs → admin_*.rs (5 modules) - designer/canvas.rs → canvas_api/ (6 modules) - designer/mod.rs → designer_api/ (6 modules) - docs/handlers.rs → handlers_api/ (11 modules) - drive/mod.rs → drive_handlers.rs, drive_types.rs - learn/mod.rs → types.rs - main.rs → main_module/ (7 modules) - meet/webinar.rs → webinar_api/ (8 modules) - paper/mod.rs → (10 modules) - security/auth.rs → auth_api/ (7 modules) - security/passkey.rs → (4 modules) - sources/mod.rs → sources_api/ (5 modules) - tasks/mod.rs → task_api/ (5 modules) Stats: 38,040 deletions, 1,315 additions across 318 files Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
127 lines
3.9 KiB
Rust
127 lines
3.9 KiB
Rust
use crate::core::shared::state::AppState;
|
|
use crate::docs::storage::{get_current_user_id, load_document_from_drive, save_document};
|
|
use crate::docs::types::{GenerateTocRequest, TableOfContents, TocEntry, TocResponse, UpdateTocRequest};
|
|
use crate::docs::utils::strip_html;
|
|
use axum::{
|
|
extract::State,
|
|
http::StatusCode,
|
|
Json,
|
|
};
|
|
use chrono::Utc;
|
|
use std::sync::Arc;
|
|
|
|
pub async fn handle_generate_toc(
|
|
State(state): State<Arc<AppState>>,
|
|
Json(req): Json<GenerateTocRequest>,
|
|
) -> Result<Json<TocResponse>, (StatusCode, Json<serde_json::Value>)> {
|
|
let user_id = get_current_user_id();
|
|
let mut doc = match load_document_from_drive(&state, &user_id, &req.doc_id).await {
|
|
Ok(Some(d)) => d,
|
|
Ok(None) => {
|
|
return Err((
|
|
StatusCode::NOT_FOUND,
|
|
Json(serde_json::json!({ "error": "Document not found" })),
|
|
))
|
|
}
|
|
Err(e) => {
|
|
return Err((
|
|
StatusCode::INTERNAL_SERVER_ERROR,
|
|
Json(serde_json::json!({ "error": e })),
|
|
))
|
|
}
|
|
};
|
|
|
|
let mut entries = Vec::new();
|
|
let content = &doc.content;
|
|
|
|
|
|
for level in 1..=req.max_level {
|
|
let tag = format!("<h{level}>");
|
|
let end_tag = format!("</h{level}>");
|
|
let mut search_pos = 0;
|
|
|
|
while let Some(start) = content[search_pos..].find(&tag) {
|
|
let abs_start = search_pos + start;
|
|
if let Some(end) = content[abs_start..].find(&end_tag) {
|
|
let text_start = abs_start + tag.len();
|
|
let text_end = abs_start + end;
|
|
let text = strip_html(&content[text_start..text_end]);
|
|
|
|
entries.push(TocEntry {
|
|
id: uuid::Uuid::new_v4().to_string(),
|
|
text,
|
|
level,
|
|
page_number: None,
|
|
position: abs_start,
|
|
});
|
|
search_pos = text_end + end_tag.len();
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
entries.sort_by_key(|e| e.position);
|
|
|
|
let toc = TableOfContents {
|
|
id: uuid::Uuid::new_v4().to_string(),
|
|
title: "Table of Contents".to_string(),
|
|
entries,
|
|
max_level: req.max_level,
|
|
show_page_numbers: req.show_page_numbers,
|
|
use_hyperlinks: req.use_hyperlinks,
|
|
};
|
|
|
|
doc.toc = Some(toc.clone());
|
|
doc.updated_at = Utc::now();
|
|
|
|
if let Err(e) = save_document(&state, &user_id, &doc).await {
|
|
return Err((
|
|
StatusCode::INTERNAL_SERVER_ERROR,
|
|
Json(serde_json::json!({ "error": e })),
|
|
));
|
|
}
|
|
|
|
Ok(Json(TocResponse { toc }))
|
|
}
|
|
|
|
pub async fn handle_update_toc(
|
|
State(state): State<Arc<AppState>>,
|
|
Json(req): Json<UpdateTocRequest>,
|
|
) -> Result<Json<TocResponse>, (StatusCode, Json<serde_json::Value>)> {
|
|
let user_id = get_current_user_id();
|
|
let doc = match load_document_from_drive(&state, &user_id, &req.doc_id).await {
|
|
Ok(Some(d)) => d,
|
|
Ok(None) => {
|
|
return Err((
|
|
StatusCode::NOT_FOUND,
|
|
Json(serde_json::json!({ "error": "Document not found" })),
|
|
))
|
|
}
|
|
Err(e) => {
|
|
return Err((
|
|
StatusCode::INTERNAL_SERVER_ERROR,
|
|
Json(serde_json::json!({ "error": e })),
|
|
))
|
|
}
|
|
};
|
|
|
|
let existing_toc = doc.toc.unwrap_or_else(|| TableOfContents {
|
|
id: uuid::Uuid::new_v4().to_string(),
|
|
title: "Table of Contents".to_string(),
|
|
entries: vec![],
|
|
max_level: 3,
|
|
show_page_numbers: true,
|
|
use_hyperlinks: true,
|
|
});
|
|
|
|
let gen_req = GenerateTocRequest {
|
|
doc_id: req.doc_id,
|
|
max_level: existing_toc.max_level,
|
|
show_page_numbers: existing_toc.show_page_numbers,
|
|
use_hyperlinks: existing_toc.use_hyperlinks,
|
|
};
|
|
|
|
handle_generate_toc(State(state), Json(gen_req)).await
|
|
}
|