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>
153 lines
4.4 KiB
Rust
153 lines
4.4 KiB
Rust
#[cfg(feature = "embed-ui")]
|
|
use axum::{
|
|
body::Body,
|
|
http::{header, Request, Response, StatusCode},
|
|
Router,
|
|
};
|
|
#[cfg(not(feature = "embed-ui"))]
|
|
use axum::Router;
|
|
#[cfg(feature = "embed-ui")]
|
|
use rust_embed::RustEmbed;
|
|
#[cfg(feature = "embed-ui")]
|
|
use std::path::Path;
|
|
|
|
#[cfg(feature = "embed-ui")]
|
|
#[derive(RustEmbed)]
|
|
#[folder = "ui"]
|
|
#[prefix = "suite"]
|
|
struct EmbeddedUi;
|
|
|
|
#[cfg(feature = "embed-ui")]
|
|
fn get_mime_type(path: &str) -> &'static str {
|
|
let ext = Path::new(path)
|
|
.extension()
|
|
.and_then(|e| e.to_str())
|
|
.unwrap_or("");
|
|
|
|
match ext {
|
|
"html" | "htm" => "text/html; charset=utf-8",
|
|
"css" => "text/css; charset=utf-8",
|
|
"js" | "mjs" => "application/javascript; charset=utf-8",
|
|
"json" => "application/json; charset=utf-8",
|
|
"png" => "image/png",
|
|
"jpg" | "jpeg" => "image/jpeg",
|
|
"gif" => "image/gif",
|
|
"svg" => "image/svg+xml",
|
|
"ico" => "image/x-icon",
|
|
"woff" => "font/woff",
|
|
"woff2" => "font/woff2",
|
|
"ttf" => "font/ttf",
|
|
"otf" => "font/otf",
|
|
"eot" => "application/vnd.ms-fontobject",
|
|
"webp" => "image/webp",
|
|
"mp4" => "video/mp4",
|
|
"webm" => "video/webm",
|
|
"mp3" => "audio/mpeg",
|
|
"wav" => "audio/wav",
|
|
"ogg" => "audio/ogg",
|
|
"pdf" => "application/pdf",
|
|
"xml" => "application/xml",
|
|
"txt" => "text/plain; charset=utf-8",
|
|
"md" => "text/markdown; charset=utf-8",
|
|
"wasm" => "application/wasm",
|
|
_ => "application/octet-stream",
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "embed-ui")]
|
|
async fn serve_embedded_file(req: Request<Body>) -> Response<Body> {
|
|
let path = req.uri().path().trim_start_matches('/');
|
|
|
|
let file_path = if path.is_empty() || path == "/" {
|
|
"index.html"
|
|
} else {
|
|
path
|
|
};
|
|
|
|
let file_path = file_path.strip_prefix("suite/").unwrap_or(file_path);
|
|
|
|
log::trace!("Serving embedded file: {}", file_path);
|
|
|
|
let try_paths = [
|
|
file_path.to_string(),
|
|
format!("{}/index.html", file_path.trim_end_matches('/')),
|
|
format!("{}.html", file_path),
|
|
];
|
|
|
|
for try_path in &try_paths {
|
|
if let Some(content) = EmbeddedUi::get(try_path) {
|
|
let mime = get_mime_type(try_path);
|
|
|
|
log::trace!("Found embedded file: {} with MIME type: {}", try_path, mime);
|
|
|
|
return Response::builder()
|
|
.status(StatusCode::OK)
|
|
.header(header::CONTENT_TYPE, mime)
|
|
.header(header::CACHE_CONTROL, "public, max-age=3600")
|
|
.body(Body::from(content.data.into_owned()))
|
|
.unwrap_or_else(|_| {
|
|
Response::builder()
|
|
.status(StatusCode::INTERNAL_SERVER_ERROR)
|
|
.body(Body::from("Internal Server Error"))
|
|
.unwrap_or_else(|_| Response::new(Body::from("Critical Error")))
|
|
});
|
|
}
|
|
}
|
|
|
|
log::warn!("Embedded file not found: {} (tried paths: {:?})", file_path, try_paths);
|
|
|
|
Response::builder()
|
|
.status(StatusCode::NOT_FOUND)
|
|
.header(header::CONTENT_TYPE, "text/html; charset=utf-8")
|
|
.body(Body::from(
|
|
r#"<!DOCTYPE html>
|
|
<html>
|
|
<head><title>404 Not Found</title></head>
|
|
<body>
|
|
<h1>404 - Not Found</h1>
|
|
<p>The requested file was not found in embedded UI.</p>
|
|
<p><a href="/">Go to Home</a></p>
|
|
</body>
|
|
</html>"#,
|
|
))
|
|
.unwrap_or_else(|_| Response::new(Body::from("500 Internal Server Error")))
|
|
}
|
|
|
|
#[cfg(feature = "embed-ui")]
|
|
pub fn embedded_ui_router() -> Router {
|
|
use axum::routing::any;
|
|
Router::new().fallback(any(serve_embedded_file))
|
|
}
|
|
|
|
#[cfg(feature = "embed-ui")]
|
|
pub fn has_embedded_ui() -> bool {
|
|
let has_index = EmbeddedUi::get("index.html").is_some();
|
|
if has_index {
|
|
log::info!("Embedded UI detected - index.html found");
|
|
} else {
|
|
log::warn!("No embedded UI found - index.html not embedded");
|
|
}
|
|
has_index
|
|
}
|
|
|
|
#[cfg(feature = "embed-ui")]
|
|
pub fn list_embedded_files() -> Vec<String> {
|
|
let files: Vec<String> = EmbeddedUi::iter().map(|f| f.to_string()).collect();
|
|
log::debug!("Embedded UI contains {} files", files.len());
|
|
files
|
|
}
|
|
|
|
#[cfg(not(feature = "embed-ui"))]
|
|
pub fn has_embedded_ui() -> bool {
|
|
false
|
|
}
|
|
|
|
#[cfg(not(feature = "embed-ui"))]
|
|
pub fn list_embedded_files() -> Vec<String> {
|
|
Vec::new()
|
|
}
|
|
|
|
#[cfg(not(feature = "embed-ui"))]
|
|
pub fn embedded_ui_router() -> Router {
|
|
Router::new()
|
|
}
|