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

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()
}