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>
178 lines
6.4 KiB
Rust
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())
|
|
}
|