generalbots/src/docs/handlers_api/toc.rs
Rodrigo Rodriguez 5ea171d126
Some checks failed
BotServer CI / build (push) Failing after 1m34s
Refactor: Split large files into modular subdirectories
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>
2026-02-12 21:09:30 +00:00

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
}