botserver/src/paper/export.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

178 lines
6.4 KiB
Rust

use aws_sdk_s3::primitives::ByteStream;
use crate::core::shared::state::AppState;
use crate::core::urls::ApiUrls;
use axum::{
extract::{Query, State},
http::HeaderMap,
response::{Html, IntoResponse},
};
use std::sync::Arc;
use super::auth::get_current_user;
use super::models::ExportQuery;
use super::storage::load_document_from_drive;
use super::utils::{format_error, html_escape, markdown_to_html, strip_markdown};
pub async fn handle_export_pdf(
State(state): State<Arc<AppState>>,
headers: HeaderMap,
Query(params): Query<ExportQuery>,
) -> impl IntoResponse {
let Ok((_user_id, user_identifier)) = get_current_user(&state, &headers).await else {
return Html(format_error("Authentication required"));
};
if let Some(doc_id) = params.id {
if let Ok(Some(_doc)) = load_document_from_drive(&state, &user_identifier, &doc_id).await {
return Html("<script>alert('PDF export started. The file will be saved to your exports folder.');</script>".to_string());
}
}
Html("<script>alert('Please save your document first.');</script>".to_string())
}
pub async fn handle_export_docx(
State(state): State<Arc<AppState>>,
headers: HeaderMap,
Query(params): Query<ExportQuery>,
) -> impl IntoResponse {
let Ok((_user_id, user_identifier)) = get_current_user(&state, &headers).await else {
return Html(format_error("Authentication required"));
};
if let Some(doc_id) = params.id {
if let Ok(Some(_doc)) = load_document_from_drive(&state, &user_identifier, &doc_id).await {
return Html("<script>alert('Word export started. The file will be saved to your exports folder.');</script>".to_string());
}
}
Html("<script>alert('Please save your document first.');</script>".to_string())
}
pub async fn handle_export_md(
State(state): State<Arc<AppState>>,
headers: HeaderMap,
Query(params): Query<ExportQuery>,
) -> impl IntoResponse {
let Ok((_user_id, user_identifier)) = get_current_user(&state, &headers).await else {
return Html(format_error("Authentication required"));
};
if let Some(doc_id) = params.id {
if let Ok(Some(doc)) = load_document_from_drive(&state, &user_identifier, &doc_id).await {
let export_path = format!(
"users/{}/exports/{}.md",
user_identifier
.replace(['/', '\\', ':', '*', '?', '"', '<', '>', '|'], "_")
.to_lowercase(),
doc.title
.replace(['/', '\\', ':', '*', '?', '"', '<', '>', '|'], "_")
);
if let Some(s3_client) = state.drive.as_ref() {
let _ = s3_client
.put_object()
.bucket(&state.bucket_name)
.key(&export_path)
.body(ByteStream::from(doc.content.into_bytes()))
.content_type("text/markdown")
.send()
.await;
}
return Html(
"<script>alert('Markdown exported to your exports folder.');</script>".to_string(),
);
}
}
Html("<script>alert('Please save your document first.');</script>".to_string())
}
pub async fn handle_export_html(
State(state): State<Arc<AppState>>,
headers: HeaderMap,
Query(params): Query<ExportQuery>,
) -> impl IntoResponse {
let Ok((_user_id, user_identifier)) = get_current_user(&state, &headers).await else {
return Html(format_error("Authentication required"));
};
if let Some(doc_id) = params.id {
if let Ok(Some(doc)) = load_document_from_drive(&state, &user_identifier, &doc_id).await {
let html_content = format!(
"<!DOCTYPE html>\n<html>\n<head>\n<title>{}</title>\n<meta charset=\"utf-8\">\n</head>\n<body>\n<article>\n{}\n</article>\n</body>\n</html>",
html_escape(&doc.title),
markdown_to_html(&doc.content)
);
let export_path = format!(
"users/{}/exports/{}.html",
user_identifier
.replace(['/', '\\', ':', '*', '?', '"', '<', '>', '|'], "_")
.to_lowercase(),
doc.title
.replace(['/', '\\', ':', '*', '?', '"', '<', '>', '|'], "_")
);
if let Some(s3_client) = state.drive.as_ref() {
let _ = s3_client
.put_object()
.bucket(&state.bucket_name)
.key(&export_path)
.body(ByteStream::from(html_content.into_bytes()))
.content_type("text/html")
.send()
.await;
}
return Html(
"<script>alert('HTML exported to your exports folder.');</script>".to_string(),
);
}
}
Html("<script>alert('Please save your document first.');</script>".to_string())
}
pub async fn handle_export_txt(
State(state): State<Arc<AppState>>,
headers: HeaderMap,
Query(params): Query<ExportQuery>,
) -> impl IntoResponse {
let Ok((_user_id, user_identifier)) = get_current_user(&state, &headers).await else {
return Html(format_error("Authentication required"));
};
if let Some(doc_id) = params.id {
if let Ok(Some(doc)) = load_document_from_drive(&state, &user_identifier, &doc_id).await {
let plain_text = strip_markdown(&doc.content);
let export_path = format!(
"users/{}/exports/{}.txt",
user_identifier
.replace(['/', '\\', ':', '*', '?', '"', '<', '>', '|'], "_")
.to_lowercase(),
doc.title
.replace(['/', '\\', ':', '*', '?', '"', '<', '>', '|'], "_")
);
if let Some(s3_client) = state.drive.as_ref() {
let _ = s3_client
.put_object()
.bucket(&state.bucket_name)
.key(&export_path)
.body(ByteStream::from(plain_text.into_bytes()))
.content_type("text/plain")
.send()
.await;
}
return Html(
"<script>alert('Text exported to your exports folder.');</script>".to_string(),
);
}
}
Html("<script>alert('Please save your document first.');</script>".to_string())
}